openzoo 0.48.63 → 0.48.64
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 +19 -17
- package/lib/proxy.js +51 -3
- package/package.json +1 -1
package/lib/brief.js
CHANGED
|
@@ -19,24 +19,22 @@
|
|
|
19
19
|
* not an API) because nothing in the conversation named the real one.
|
|
20
20
|
*/
|
|
21
21
|
export const briefFor = (selfUrl) => [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'
|
|
31
|
-
'',
|
|
32
|
-
' Explicit bind (unpaid): POST /v1/hrr/bind with {"corpus": "..."} returns {"context_id": "..."}. Passing that id as the X-HRR-Context header on later /v1/chat/completions calls lets the body stay small. Passing an existing context_id alongside a new corpus appends to it, which is how a corpus larger than one request gets bound in parts.',
|
|
33
|
-
' Request size: single requests over ~8MB are dropped by the network hop before reaching the proxy (opaque 413 or dead connection). That is a request limit, not a context limit.',
|
|
34
|
-
'',
|
|
35
|
-
'- Payment: calls are settled per request from the operator\'s own wallet via x402 (Solana / Base / Robinhood Chain, whichever is funded). There is no account to create and no key for you to supply or handle. GET / on this proxy returns the same description. /hrr/bind and GET /models are unpaid; /chat/completions is paid and uses the bearer key the client is already configured with, which is not readable from inside the conversation.',
|
|
36
|
-
'',
|
|
37
|
-
'- Model ids: unknown ids are matched to the nearest served model rather than erroring. /v1/models lists what is actually served, and each alias row carries `served_by`.',
|
|
22
|
+
// SMALL ON PURPOSE. This used to be ~2.2KB of prose on EVERY request —
|
|
23
|
+
// corpus binding, request-size limits, payment rails, model-id matching —
|
|
24
|
+
// most of which a given call never needs, all of which the caller pays for in
|
|
25
|
+
// tokens and latency. An agent that needs the detail can ask; what it cannot
|
|
26
|
+
// work out for itself is the base URL and the one behaviour that changes how
|
|
27
|
+
// it should send a big body. Everything else was documentation shipped as
|
|
28
|
+
// overhead.
|
|
29
|
+
...(selfUrl ? [`Endpoint: ${selfUrl} (already ends in /v1). Routes: /chat/completions, /hrr/bind, /models.`] : []),
|
|
30
|
+
'Bodies over ~16KB are bound to holographic memory and answered by retrieval, so a large corpus can be sent whole rather than summarised or chunked. A corpus sent once is not re-uploaded.',
|
|
31
|
+
'Calls are paid per request from the operator\'s wallet; there is no key to supply. Unknown model ids match the nearest served model.',
|
|
38
32
|
].join('\n');
|
|
39
33
|
|
|
34
|
+
/** Stable substring used to detect an already-injected brief. Must appear in
|
|
35
|
+
* briefFor() output verbatim — see injectBrief(). */
|
|
36
|
+
export const BRIEF_MARK = 'bound to holographic memory and answered by retrieval';
|
|
37
|
+
|
|
40
38
|
/** Back-compat: the briefing with no endpoint line. */
|
|
41
39
|
export const BRIEF = briefFor(null);
|
|
42
40
|
|
|
@@ -51,7 +49,11 @@ export function injectBrief(body, selfUrl = null) {
|
|
|
51
49
|
if (process.env.OPENZOO_NO_BRIEF === '1') return null;
|
|
52
50
|
const msgs = body?.messages;
|
|
53
51
|
if (!Array.isArray(msgs) || !msgs.length) return null;
|
|
54
|
-
|
|
52
|
+
// THE SENTINEL MUST BE TEXT THE BRIEF ACTUALLY CONTAINS. This checked for
|
|
53
|
+
// 'connected through an openzoo proxy' — the old opening line — so shrinking
|
|
54
|
+
// the brief would have silently broken idempotency and stacked a fresh copy
|
|
55
|
+
// onto every single turn, growing the system block without bound.
|
|
56
|
+
if (msgs.some((m) => typeof m?.content === 'string' && m.content.includes(BRIEF_MARK))) return null;
|
|
55
57
|
|
|
56
58
|
const brief = { role: 'system', content: briefFor(selfUrl) };
|
|
57
59
|
// THE LEADING SYSTEM RUN ONLY — NOT THE LAST SYSTEM ANYWHERE.
|
package/lib/proxy.js
CHANGED
|
@@ -1216,7 +1216,7 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
1216
1216
|
bodyBuf = rw.body;
|
|
1217
1217
|
}
|
|
1218
1218
|
try {
|
|
1219
|
-
|
|
1219
|
+
let parsed = JSON.parse(bodyBuf.toString('utf8'));
|
|
1220
1220
|
wantsStream = parsed?.stream === true || clientWantsStream;
|
|
1221
1221
|
// REASONING MODELS SPEND max_tokens ON THINKING FIRST.
|
|
1222
1222
|
//
|
|
@@ -1262,8 +1262,56 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
1262
1262
|
const selfUrl = viaTunnel && tunnelGate?.publicUrl
|
|
1263
1263
|
? `${tunnelGate.publicUrl}/v1`
|
|
1264
1264
|
: `http://localhost:${config.port}/v1`;
|
|
1265
|
-
|
|
1266
|
-
|
|
1265
|
+
// NOT ON A YES/NO. The brief is ~2.2KB describing corpus binding,
|
|
1266
|
+
// request-size limits and payment — none of which a tiny call can
|
|
1267
|
+
// use. Claude Code's auto-mode safety classifier asks a 16-token
|
|
1268
|
+
// question before it will run Bash, and it has a short timeout:
|
|
1269
|
+
// MEASURED, that call takes 3.5s cold through here against a 0.09s
|
|
1270
|
+
// gateway 402, and it times out on a machine paying on-chain. Adding
|
|
1271
|
+
// 2.2KB of prose to a body that small is latency and tokens spent on
|
|
1272
|
+
// advice nobody will read.
|
|
1273
|
+
//
|
|
1274
|
+
// Threshold is the same one the spill uses: below it there is no
|
|
1275
|
+
// corpus and nothing the brief could help with.
|
|
1276
|
+
const tiny = bodyBuf.length < BIND_MIN_CHARS
|
|
1277
|
+
&& Number(parsed?.max_tokens ?? 0) > 0
|
|
1278
|
+
&& Number(parsed?.max_tokens) <= 64;
|
|
1279
|
+
const briefed = tiny ? null : injectBrief(parsed, selfUrl);
|
|
1280
|
+
if (briefed) parsed = briefed;
|
|
1281
|
+
// SYSTEM MESSAGES BELONG AT THE FRONT, OR GOOGLE 400s.
|
|
1282
|
+
//
|
|
1283
|
+
// Claude Code emits <system-reminder> blocks mid-conversation, which
|
|
1284
|
+
// is legal for Anthropic natively. Several upstreams behind OpenRouter
|
|
1285
|
+
// are not: fable-5 is served by GOOGLE, whose API takes a system
|
|
1286
|
+
// instruction only before the conversation starts and rejects one
|
|
1287
|
+
// after. CAPTURED live — provider_error code 400,
|
|
1288
|
+
// roles="sssusatatus", 311KB body: two system messages sitting after
|
|
1289
|
+
// user turns, on a model that answers a simple call fine.
|
|
1290
|
+
//
|
|
1291
|
+
// So fold every later system message into the leading block, in
|
|
1292
|
+
// order. The content survives and its position moves; the alternative
|
|
1293
|
+
// is a 400 that ends the turn and tells the caller nothing.
|
|
1294
|
+
const nm = Array.isArray(parsed?.messages) ? parsed.messages : null;
|
|
1295
|
+
if (nm && nm.length > 1) {
|
|
1296
|
+
let lead = 0;
|
|
1297
|
+
while (lead < nm.length && nm[lead]?.role === 'system') lead += 1;
|
|
1298
|
+
const strays = [];
|
|
1299
|
+
const kept = [];
|
|
1300
|
+
nm.forEach((m, i) => {
|
|
1301
|
+
if (i >= lead && m?.role === 'system') strays.push(m);
|
|
1302
|
+
else kept.push(m);
|
|
1303
|
+
});
|
|
1304
|
+
if (strays.length) {
|
|
1305
|
+
const merged = strays.map((m) => (typeof m.content === 'string' ? m.content : msgText(m))).filter(Boolean).join('\n\n');
|
|
1306
|
+
const head = kept.slice(0, lead);
|
|
1307
|
+
const tailMsgs = kept.slice(lead);
|
|
1308
|
+
if (head.length) head[head.length - 1] = { ...head[head.length - 1], content: `${typeof head[head.length - 1].content === 'string' ? head[head.length - 1].content : msgText(head[head.length - 1])}\n\n${merged}` };
|
|
1309
|
+
else head.push({ role: 'system', content: merged });
|
|
1310
|
+
parsed = { ...parsed, messages: [...head, ...tailMsgs] };
|
|
1311
|
+
log(`hoisted ${strays.length} interleaved system message(s) to the leading block (some providers 400 otherwise)`);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
bodyBuf = Buffer.from(JSON.stringify(parsed));
|
|
1267
1315
|
}
|
|
1268
1316
|
} catch { /* not JSON */ }
|
|
1269
1317
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.64",
|
|
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",
|