openzoo 0.48.96 → 0.48.97

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/lib/livestatus.js CHANGED
@@ -35,32 +35,41 @@ export function formatRaceStatus(back, need) {
35
35
  return `racing ${b}/${n} back…`;
36
36
  }
37
37
 
38
- /** Real answers count toward X. Empty and HTTP/pay/timeout notes do not. */
39
- export function isRaceCountable(text) {
40
- const s = String(text || '').trim();
41
- if (!s) return false;
42
- return !/^\((?:upstream error|request failed|payment failed|rate limited|stream timed out|stream stalled)/i.test(s);
43
- }
38
+ /** Race-level failure when no countable answer exists. Never a single model name. */
39
+ export const RACE_EVERY_FAILED = '(race: every model failed — no reply)';
40
+
41
+ const RACE_HTTP_NOTE = /^\((?:upstream error|request failed|payment failed|rate limited|stream timed out|stream stalled)/i;
42
+ const RACE_MODEL_FAILED = /^\([^)]+ (?:failed:|returned nothing)/i;
43
+ const RACE_FETCH_FAILED = /^(?:typeerror:\s*)?fetch failed$/i;
44
44
 
45
- function shortModel(id) {
46
- const s = String(id || '');
47
- if (!s) return '';
48
- return s.includes('/') ? s.split('/').pop() : s;
45
+ /**
46
+ * Real answers count toward X.
47
+ * Empty, HTTP/pay/timeout notes, TypeError `fetch failed`, `(model failed: …)`,
48
+ * and any arrival with `.error` do not — those racers are abandoned.
49
+ * Accepts a string or `{ text, error }`.
50
+ */
51
+ export function isRaceCountable(textOrArrival) {
52
+ const arrival = textOrArrival && typeof textOrArrival === 'object' && !Array.isArray(textOrArrival)
53
+ ? textOrArrival
54
+ : { text: textOrArrival };
55
+ if (arrival.error) return false;
56
+ const s = String(arrival.text || '').trim();
57
+ if (!s) return false;
58
+ if (RACE_FETCH_FAILED.test(s)) return false;
59
+ if (RACE_HTTP_NOTE.test(s)) return false;
60
+ if (RACE_MODEL_FAILED.test(s)) return false;
61
+ return true;
49
62
  }
50
63
 
51
64
  /**
52
- * Last completion that arrived, even if empty-ish or error-y.
53
- * Never blank: synthesize a visible error when nobody produced text.
65
+ * Fallback when X never fills: one race-level error, not `(one-model failed: …)`.
66
+ * Failed arrivals are abandoned, not shipped as the assistant message.
54
67
  */
55
68
  export function raceLastShip(arrivals) {
56
69
  const list = Array.isArray(arrivals) ? arrivals : [];
57
- const last = list[list.length - 1];
58
- if (!last) return { model: '', text: '(race: every model failed — no reply)', error: true };
59
- const raw = String(last.text || '');
60
- if (raw.trim()) return { ...last, text: raw };
61
- const who = shortModel(last.model) || 'model';
62
- if (last.error) return { ...last, text: `(${who} failed: ${last.error})`, error: true };
63
- return { ...last, text: `(${who} returned nothing)`, error: true };
70
+ const last = [...list].reverse().find((a) => isRaceCountable(a));
71
+ if (last) return { ...last, text: String(last.text) };
72
+ return { model: '', text: RACE_EVERY_FAILED, error: true };
64
73
  }
65
74
 
66
75
  /** Default bar a classified race answer must clear (0–10). Overridable. */
@@ -147,7 +156,7 @@ export function createRaceFeed(onDelta, onStatus, need) {
147
156
  settled = true;
148
157
  const text = String(winner?.text || '').trim()
149
158
  ? winner.text
150
- : '(race: every model failed — no reply)';
159
+ : RACE_EVERY_FAILED;
151
160
  // Live stream already showing this answer — keep going, do not re-dump.
152
161
  if (winner?.model && live === winner.model && !winner.error) return;
153
162
  live = winner?.model || live;
package/lib/podagent.mjs CHANGED
@@ -641,9 +641,9 @@ export async function tierModels(tier, n = 1, random = false) {
641
641
  * (correctness, completeness, actually did the asked thing — a RUN:/DONE:
642
642
  * directive is success, not a flaw). Highest score that clears the bar wins;
643
643
  * a tie is pairwise-broken. If nobody clears, the last of the X is shipped
644
- * anyway. If X never fills (every entrant empty/5xx), ship the last
645
- * completion that arrived even if empty-ish or error-y. If nobody produced
646
- * any text, surface a real error in the bubble. Never blank, never hang.
644
+ * anyway. If X never fills (every entrant empty/5xx/fetch-failed), ship a
645
+ * race-level errornever a single model's `(name failed: fetch failed)` as
646
+ * if it won. Never blank, never hang.
647
647
  *
648
648
  * Every entrant is paid for, including the abandoned one — this trades money
649
649
  * for latency and quality, which is why it is opt-in and capped.
@@ -679,9 +679,10 @@ export async function brainRace(messages, onDelta, contextId, models, need = 1,
679
679
  const attempts = list.map((m) => stream(messages, (chunk) => feed.onToken(m, chunk), contextId, m, maxTokens)
680
680
  .then((text) => {
681
681
  const raw = text == null ? '' : String(text);
682
- arrivals.push({ model: m, text: raw });
683
- if (isRaceCountable(raw)) {
684
- done.push({ model: m, text: raw });
682
+ const arrival = { model: m, text: raw };
683
+ arrivals.push(arrival);
684
+ if (isRaceCountable(arrival)) {
685
+ done.push(arrival);
685
686
  feed.onBack();
686
687
  } else {
687
688
  feed.onFail(m);
@@ -704,8 +705,8 @@ export async function brainRace(messages, onDelta, contextId, models, need = 1,
704
705
 
705
706
  await enough;
706
707
  // Completion order, so this really is the first X back — not the first X
707
- // launched. A slow 3rd never enters this set. Empty/5xx stay in arrivals
708
- // so we can still ship the last one if X never fills.
708
+ // launched. A slow 3rd never enters this set. Empty/5xx/fetch-failed stay
709
+ // in arrivals only so an all-fail race can ship one race-level error.
709
710
  const cands = done.slice(0, want);
710
711
  if (!cands.length) return ship(raceLastShip(arrivals));
711
712
  // One real answer — nothing to compare. Ship it; do not spend a classify
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.48.96",
3
+ "version": "0.48.97",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun \u2014 point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",