openzoo 0.48.58 → 0.48.59
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/proxy.js +59 -1
- package/package.json +1 -1
package/lib/proxy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, appendFileSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
4
5
|
import http from 'node:http';
|
|
5
6
|
import crypto from 'node:crypto';
|
|
6
7
|
import { Readable } from 'node:stream';
|
|
@@ -115,6 +116,9 @@ const mb = (n) => (n / 1048576).toFixed(1);
|
|
|
115
116
|
|
|
116
117
|
// anchor -> { corpus, contextId, hash } for append-only transcript spills
|
|
117
118
|
const spillMemo = new Map();
|
|
119
|
+
// path:mtime of every file already bound — a file is bound once per version,
|
|
120
|
+
// never re-uploaded because the agent read it again.
|
|
121
|
+
const boundFiles = new Set();
|
|
118
122
|
|
|
119
123
|
/**
|
|
120
124
|
* Every fundable balance across all three chains, for the startup line and
|
|
@@ -463,7 +467,61 @@ async function spillTranscript(body, log, req) {
|
|
|
463
467
|
if (lastUser > firstSpillable && cut > lastUser) cut = lastUser;
|
|
464
468
|
|
|
465
469
|
const head = msgs.slice(0, firstSpillable); // system block, always kept
|
|
466
|
-
|
|
470
|
+
// EVERY FILE THE AGENT TOUCHED, AT FULL SIZE.
|
|
471
|
+
//
|
|
472
|
+
// The saving ratio is corpus/sent, so on a fresh session — where the corpus
|
|
473
|
+
// IS the conversation — it starts near 1x and only climbs as you talk. That
|
|
474
|
+
// is exactly what the live agent numbers showed: 1.0-1.3x early, 8x once a
|
|
475
|
+
// 149k-token history existed. MEASURED the same day: prompt compressed 2.67x
|
|
476
|
+
// but billed savings was 1.13x, because output does not compress and a small
|
|
477
|
+
// corpus leaves nothing to compress on the other side either.
|
|
478
|
+
//
|
|
479
|
+
// Files are the fix. An agent reads far more bytes than it discusses, the
|
|
480
|
+
// harness has usually TRUNCATED them on the way in, and the full text is
|
|
481
|
+
// sitting on this machine. Binding it makes the corpus large immediately
|
|
482
|
+
// instead of eventually, and makes the truncated read whole again.
|
|
483
|
+
//
|
|
484
|
+
// Read-only, bounded, deduped by path+mtime, and failures are silent: this
|
|
485
|
+
// runs on the request path and must never be the reason a turn does not go.
|
|
486
|
+
const filesForCorpus = () => {
|
|
487
|
+
if (process.env.OPENZOO_BIND_FILES === '0') return '';
|
|
488
|
+
const cap = Number(process.env.OPENZOO_BIND_FILE_MAX || 400_000);
|
|
489
|
+
const out = [];
|
|
490
|
+
const seen = new Set();
|
|
491
|
+
for (const m of msgs) {
|
|
492
|
+
const blocks = Array.isArray(m?.content) ? m.content : [];
|
|
493
|
+
const calls = Array.isArray(m?.tool_calls) ? m.tool_calls : [];
|
|
494
|
+
const paths = [];
|
|
495
|
+
for (const b of blocks) {
|
|
496
|
+
const p = b?.input?.file_path || b?.input?.path;
|
|
497
|
+
if (typeof p === 'string') paths.push(p);
|
|
498
|
+
}
|
|
499
|
+
for (const c of calls) {
|
|
500
|
+
try {
|
|
501
|
+
const a = JSON.parse(c?.function?.arguments || '{}');
|
|
502
|
+
if (typeof a.file_path === 'string') paths.push(a.file_path);
|
|
503
|
+
else if (typeof a.path === 'string') paths.push(a.path);
|
|
504
|
+
} catch { /* not json args */ }
|
|
505
|
+
}
|
|
506
|
+
for (const p of paths) {
|
|
507
|
+
if (seen.has(p) || !path.isAbsolute(p)) continue;
|
|
508
|
+
seen.add(p);
|
|
509
|
+
try {
|
|
510
|
+
const st = fs.statSync(p);
|
|
511
|
+
if (!st.isFile() || st.size > cap) continue;
|
|
512
|
+
const key = `${p}:${st.mtimeMs}`;
|
|
513
|
+
if (boundFiles.has(key)) continue;
|
|
514
|
+
boundFiles.add(key);
|
|
515
|
+
out.push(`FILE ${p}\n${fs.readFileSync(p, 'utf8')}`);
|
|
516
|
+
} catch { /* unreadable, gone, or binary — simply not corpus */ }
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return out.join('\n\n');
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
const turns = msgs.slice(firstSpillable, cut).map(msgText).filter(Boolean).join('\n\n');
|
|
523
|
+
const files = filesForCorpus();
|
|
524
|
+
const corpus = files ? `${turns}\n\n${files}` : turns;
|
|
467
525
|
if (corpus.length <= BIND_MIN_CHARS) return null;
|
|
468
526
|
|
|
469
527
|
// CONTINUE THE CONTEXT, BIND ONLY THE DELTA.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.59",
|
|
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",
|