openzoo 0.51.25 → 0.51.27

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.
Files changed (2) hide show
  1. package/lib/proxy.js +27 -19
  2. package/package.json +1 -1
package/lib/proxy.js CHANGED
@@ -998,27 +998,35 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
998
998
  result = await client.fetch(url, init);
999
999
  }
1000
1000
  }
1001
- // A 5xx from the gateway: its upstream errored on a retired/renamed door
1002
- // — e.g. gpt-6-astra maps to an upstream id the provider 404s
1003
- // (`unknown model openai-gpt-6-astra`). It comes back BOTH ways: an
1004
- // UNPAID 500 (the id cannot be priced, so no x402 quote is even reached)
1005
- // and a PAID 502 (settled, then the upstream call failed). Left raw,
1006
- // either makes the harness storm retries ("Reconnecting 5/5"). Route
1007
- // once to the x402 door and gate the model 60s, same as the credits
1008
- // outage above. 501/505 excluded — shape/protocol errors a different
1009
- // model would hit too.
1001
+ // A 5xx IS NOT A REASON TO CHANGE THE MODEL. An earlier build routed any
1002
+ // gateway 5xx to OPENZOO_OUTAGE_FALLBACK and gated the model 60s. That
1003
+ // was wrong twice over: `/model fable-5.1` then reported `x402 door for
1004
+ // grok-4.6 failed`, so the caller read an error for a model they never
1005
+ // picked, and a silent substitution bills them for a DIFFERENT model's
1006
+ // answer than the one they chose and are reading. When the requested
1007
+ // model's door is down, say so about THAT model and let the caller
1008
+ // decide. Only the 402 "insufficient credits" branch above still
1009
+ // reroutes, because there the gateway itself refunds and the request
1010
+ // was never served at all.
1010
1011
  const RETRYABLE_5XX = new Set([500, 502, 503, 504]);
1011
- if (!usedFallback && FALLBACK_MODEL && outageKey(init) !== FALLBACK_MODEL
1012
- && RETRYABLE_5XX.has(result.response?.status)) {
1013
- const st = result.response.status;
1012
+ if (RETRYABLE_5XX.has(result.response?.status)) {
1014
1013
  let detail = '';
1015
- try { const j = await result.response.clone().json(); detail = String(j?.error?.message || j?.error || '').slice(0, 120); } catch { /* opaque 5xx */ }
1016
- const msg = `openzoo gateway upstream errored (HTTP ${st}${detail ? `: ${detail}` : ''}) for ${outageKey(init)}. Routing to ${FALLBACK_MODEL} (x402 door) for 60s.`;
1017
- upstreamOutage_.set(outageKey(init), { until: Date.now() + 60_000, msg });
1018
- log(`upstream ${st}: ${outageKey(init)} -> ${FALLBACK_MODEL}${detail ? ` (${detail})` : ''}`);
1019
- init = withModel(init, FALLBACK_MODEL);
1020
- usedFallback = true;
1021
- result = await client.fetch(url, init);
1014
+ try { const j = await result.response.clone().json(); detail = String(j?.error?.message || j?.error || '').slice(0, 300); } catch { /* opaque 5xx */ }
1015
+ log(`upstream ${result.response.status} for ${outageKey(init)}${detail ? `: ${detail}` : ''} — surfacing as-is, model NOT swapped`);
1016
+ // "no door quoted <model>: … wrong asset/rail (base)" is a DOOR-SIDE
1017
+ // quote failure, and measurement says it is TRANSIENT — not an empty
1018
+ // wallet and not a dead model. 2026-09-13, five minutes apart on the
1019
+ // same funded wallet: grok-4.6, deepseek-v4-flash and claude-fable-5-1
1020
+ // all 502'd at 264.68 USDC, then all returned 200 at 259.00 USDC. The
1021
+ // balance went DOWN across the recovery, so funding was never the
1022
+ // variable. Do NOT print fund-me copy here — that sends a caller with
1023
+ // $260 to a top-up page. Just name the model they asked for and say
1024
+ // the door failed to quote, so a retry is the obvious next move.
1025
+ if (/no door quoted|wrong asset\/rail/i.test(detail)) {
1026
+ const msg = `${outageKey(init)}: the x402 door failed to quote this model, so the request never reached it and nothing was charged. This is usually transient — retry in a minute. Gateway said: ${detail}`;
1027
+ jsonErr(res, 503, msg);
1028
+ return;
1029
+ }
1022
1030
  }
1023
1031
  // A PAID 400 `invalid_tools` on a Responses body we did not flatten:
1024
1032
  // this model is a function-tools-only door we had not learned yet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.25",
3
+ "version": "0.51.27",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — 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",