openzoo 0.9.9 → 0.10.1
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/brief.js +5 -1
- package/lib/proxy.js +33 -1
- package/package.json +1 -1
package/lib/brief.js
CHANGED
|
@@ -16,7 +16,11 @@ export const BRIEF = [
|
|
|
16
16
|
'',
|
|
17
17
|
'1. CONTEXT IS EFFECTIVELY UNBOUNDED. Any model here accepts corpora far past its own attention window: bodies over ~16KB are automatically carved and bound to a holographic (HRR) memory before the model sees them, and you then query against that. `context_length` in /v1/models is the client-usable ceiling (128M tokens), not the transformer window (that is `max_model_len`). DO NOT summarise, truncate, or "chunk to fit" a corpus to preserve context — send it whole and ask your question. One POST should stay under ~9.8M tokens (~32MiB) or the edge rejects it; for more than that, bind in several calls.',
|
|
18
18
|
'',
|
|
19
|
-
'2. THE BODY NEVER SHIPS TWICE. Put the corpus first, then a blank line, then your question. The corpus binds ONCE and every later question that reuses it ships only the question — near-free, and much faster. Re-pasting the same corpus each turn wastes real money.
|
|
19
|
+
'2. THE BODY NEVER SHIPS TWICE. Put the corpus first, then a blank line, then your question. The corpus binds ONCE and every later question that reuses it ships only the question — near-free, and much faster. Re-pasting the same corpus each turn wastes real money.',
|
|
20
|
+
'',
|
|
21
|
+
' EXPLICIT BIND (free, no payment): POST /v1/hrr/bind with {"corpus": "..."} returns {"context_id": "..."}. Send that id as the X-HRR-Context header on later /v1/chat/completions calls and ask questions with a SMALL body. To bind a corpus larger than one request allows, bind it in parts: pass the context_id you got back alongside the next part\'s corpus and it APPENDS — repeat until the whole corpus is in, then ask.',
|
|
22
|
+
' Keep any single request under ~8MB. Bigger bodies are dropped by the network hop before they reach the proxy (an opaque 413 or a dead connection). This is a REQUEST size limit, not a context limit — the bound context can be far larger, which is what parts are for.',
|
|
23
|
+
' Paths: your base_url already ends in /v1, so post to {base_url}/hrr/bind — NOT {base_url}/v1/hrr/bind (that double /v1 404s; the proxy repairs it, but do not rely on that).',
|
|
20
24
|
'',
|
|
21
25
|
'3. PAYMENT IS HANDLED. Every call is paid per-request from the operator\'s wallet via x402 (Solana / Base / Robinhood Chain, whichever is funded). There is no API key to find, no account, no rate limit to negotiate. Never search the operator\'s machine for credentials or endpoints — GET / on this proxy returns everything discoverable about it.',
|
|
22
26
|
'',
|
package/lib/proxy.js
CHANGED
|
@@ -15,11 +15,31 @@ import { injectBrief } from './brief.js';
|
|
|
15
15
|
const HOP_BY_HOP = new Set([
|
|
16
16
|
'host', 'connection', 'keep-alive', 'transfer-encoding', 'upgrade',
|
|
17
17
|
'proxy-authorization', 'proxy-connection', 'te', 'trailer', 'content-length',
|
|
18
|
+
// `Expect: 100-continue` is sent by curl and most HTTP libraries once a body
|
|
19
|
+
// passes ~1KB. undici REFUSES it outright ("expect header not supported"),
|
|
20
|
+
// so forwarding it made every LARGE-body request fail while small ones
|
|
21
|
+
// worked — i.e. it broke exactly the corpus calls this proxy exists for.
|
|
22
|
+
'expect',
|
|
18
23
|
// The harness's api key (sk-openzoo or anything) is accepted and dropped:
|
|
19
24
|
// the zoo takes payment, not keys.
|
|
20
25
|
'authorization',
|
|
21
26
|
]);
|
|
22
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Be forgiving about the path, the same way we are about model ids.
|
|
30
|
+
* Harnesses are configured with a base_url that ALREADY ends in /v1, so an
|
|
31
|
+
* agent building "{base}/v1/hrr/bind" sends /v1/v1/hrr/bind and gets a 404 it
|
|
32
|
+
* cannot diagnose (observed: agent concluded the bind endpoint "is not
|
|
33
|
+
* functioning as advertised" and fell back to stuffing the corpus inline).
|
|
34
|
+
* Collapse repeated /v1 and add a missing one.
|
|
35
|
+
*/
|
|
36
|
+
function normalizePath(url) {
|
|
37
|
+
const [path, query] = (url || '/').split(/(?=\?)/);
|
|
38
|
+
let p = path.replace(/^(?:\/v1)+(?=\/v1\/)/, ''); // /v1/v1/x -> /v1/x
|
|
39
|
+
if (!/^\/v1(\/|$)/.test(p) && /^\/(hrr|chat|models|completions|embeddings|usage)/.test(p)) p = `/v1${p}`;
|
|
40
|
+
return p === path ? url : `${p}${query || ''}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
23
43
|
function upstreamHeaders(req) {
|
|
24
44
|
const out = {};
|
|
25
45
|
for (const [k, v] of Object.entries(req.headers)) {
|
|
@@ -251,6 +271,11 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
251
271
|
let tunnelGate = null;
|
|
252
272
|
|
|
253
273
|
const server = http.createServer(async (req, res) => {
|
|
274
|
+
const normalized = normalizePath(req.url);
|
|
275
|
+
if (normalized !== req.url) {
|
|
276
|
+
log(`path ${req.url} -> ${normalized} (base_url already ends in /v1)`);
|
|
277
|
+
req.url = normalized;
|
|
278
|
+
}
|
|
254
279
|
const url = `${config.apiBase}${req.url}`;
|
|
255
280
|
// Requests that arrived over the public quick-tunnel URL carry cloudflared's
|
|
256
281
|
// headers; nothing dialing 127.0.0.1 directly does. That distinction is what
|
|
@@ -454,7 +479,14 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
454
479
|
log(err.message);
|
|
455
480
|
jsonErr(res, 402, err.message);
|
|
456
481
|
} else {
|
|
457
|
-
|
|
482
|
+
// "fetch failed" alone is undiagnosable — undici hides the real
|
|
483
|
+
// network error in `cause`. Surface it (and log the stack) or every
|
|
484
|
+
// transport hiccup looks identical to a payment bug.
|
|
485
|
+
const cause = err.cause?.message || err.cause?.code || err.cause;
|
|
486
|
+
const detail = cause ? `${err.message} (${cause})` : err.message;
|
|
487
|
+
log(`proxy error: ${detail}`);
|
|
488
|
+
if (process.env.OPENZOO_DEBUG) console.error(err.stack);
|
|
489
|
+
jsonErr(res, 502, `openzoo proxy error: ${detail}`);
|
|
458
490
|
}
|
|
459
491
|
}
|
|
460
492
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
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",
|