openzoo 0.48.71 → 0.48.74
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/models.js +131 -6
- package/lib/proxy.js +156 -263
- package/lib/spill.js +738 -26
- package/package.json +1 -1
package/lib/models.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { config } from './config.js';
|
|
2
2
|
|
|
3
|
+
/** Same threshold as BIND_MIN_CHARS in hrr.js — kept local so this
|
|
4
|
+
* module stays importable without the wallet/rpc stack. */
|
|
5
|
+
const BIND_MIN_CHARS = Number(process.env.OPENZOO_CONTEXT_MIN_CHARS || 16384);
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Model-id rewriting — "any harness, zero model setup".
|
|
5
9
|
*
|
|
@@ -257,11 +261,124 @@ export function rewritablePath(method, url) {
|
|
|
257
261
|
return !/embed|audio|image|moderation/.test(p);
|
|
258
262
|
}
|
|
259
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Families that spend max_tokens on hidden thinking first. A 16/40/160
|
|
266
|
+
* budget on these returns an empty visible completion — measured on Grok
|
|
267
|
+
* and DeepSeek. The raise in raiseReasoningMaxTokens exists for that.
|
|
268
|
+
* It must NEVER fire on Claude Code's 16-token auto-mode classifier.
|
|
269
|
+
*/
|
|
270
|
+
export const REASONING_MODEL_RE = /(deepseek|grok|o[134](-|$)|reasoner|thinking|-pro\b|sol-pro|qwq)/i;
|
|
271
|
+
|
|
272
|
+
/** Claude Code auto-mode classify is max_tokens=16; treat anything in (0, 64] as one. */
|
|
273
|
+
export const CLASSIFY_MAX_TOKENS = 64;
|
|
274
|
+
|
|
275
|
+
const CLASSIFIER_PREFS = ['google/gemini-3.7-flash', 'anthropic/claude-haiku-4.5'];
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Tiny yes/no classify: small body (same BIND_MIN threshold the spill/brief
|
|
279
|
+
* already use) AND a short max_tokens. Detected from the ORIGINAL body,
|
|
280
|
+
* before rewrite or the reasoning floor — otherwise OPENZOO_DEFAULT_MODEL
|
|
281
|
+
* can land the classify on Grok/DeepSeek and the floor turns 16 into 4000.
|
|
282
|
+
*
|
|
283
|
+
* `body` may be the raw Buffer/string or a parsed object. An optional
|
|
284
|
+
* `bodyLen` overrides stringify length when the caller still has the wire
|
|
285
|
+
* bytes (the proxy does).
|
|
286
|
+
*/
|
|
287
|
+
export function isTinyClassify(body, bodyLen) {
|
|
288
|
+
let parsed = body;
|
|
289
|
+
let len = bodyLen;
|
|
290
|
+
if (body == null) return false;
|
|
291
|
+
if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
292
|
+
const buf = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
293
|
+
len = buf.length;
|
|
294
|
+
try { parsed = JSON.parse(buf.toString('utf8')); } catch { return false; }
|
|
295
|
+
} else if (len == null) {
|
|
296
|
+
try { len = Buffer.byteLength(JSON.stringify(body)); } catch { return false; }
|
|
297
|
+
}
|
|
298
|
+
const mt = Number(parsed?.max_tokens);
|
|
299
|
+
return len < BIND_MIN_CHARS && Number.isFinite(mt) && mt > 0 && mt <= CLASSIFY_MAX_TOKENS;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Fast non-reasoning id that is actually on the zoo. Prefer an explicit
|
|
304
|
+
* OPENZOO_CLASSIFIER_MODEL, then flash, then haiku, then the first catalog
|
|
305
|
+
* id that does not match the reasoning regex.
|
|
306
|
+
*/
|
|
307
|
+
export function pickClassifierModel(ids, preferred = process.env.OPENZOO_CLASSIFIER_MODEL) {
|
|
308
|
+
if (!Array.isArray(ids) || !ids.length) return null;
|
|
309
|
+
if (preferred && ids.includes(preferred)) return preferred;
|
|
310
|
+
for (const id of CLASSIFIER_PREFS) {
|
|
311
|
+
if (ids.includes(id)) return id;
|
|
312
|
+
}
|
|
313
|
+
return ids.find((id) => !REASONING_MODEL_RE.test(id)) || null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Raise max_tokens for a reasoning model. Returns { parsed, raised, from, to }.
|
|
318
|
+
* Does not itself decide whether a request is a classify — callers skip this
|
|
319
|
+
* when isTinyClassify is true. Real Grok/DeepSeek chats still need the floor:
|
|
320
|
+
* 4× a caller's 40 is 160, and those still come back blank.
|
|
321
|
+
*/
|
|
322
|
+
export function raiseReasoningMaxTokens(parsed, env = process.env) {
|
|
323
|
+
const mult = Number(env.OPENZOO_REASONING_MAX_TOKENS_X || 4);
|
|
324
|
+
const cap = Number(env.OPENZOO_REASONING_MAX_TOKENS_CAP || 32000);
|
|
325
|
+
const floor = Number(env.OPENZOO_REASONING_MIN_TOKENS || 4000);
|
|
326
|
+
const mdl = String(parsed?.model || '');
|
|
327
|
+
const mt = Number(parsed?.max_tokens);
|
|
328
|
+
if (mult > 1 && REASONING_MODEL_RE.test(mdl) && Number.isFinite(mt) && mt > 0 && mt < cap) {
|
|
329
|
+
const raised = Math.min(cap, Math.max(floor, Math.round(mt * mult)));
|
|
330
|
+
if (raised > mt) {
|
|
331
|
+
return { parsed: { ...parsed, max_tokens: raised }, raised: true, from: mt, to: raised };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return { parsed, raised: false, from: mt, to: mt };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Model + max_tokens policy for one chat body.
|
|
339
|
+
*
|
|
340
|
+
* Tiny classify: pin to a fast non-reasoning catalog id, leave max_tokens
|
|
341
|
+
* alone, ignore OPENZOO_DEFAULT_MODEL. Everything else: resolveModel (which
|
|
342
|
+
* honours the default) then the reasoning floor.
|
|
343
|
+
*/
|
|
344
|
+
export function rewriteChatModel(parsed, ids, { bodyLen } = {}) {
|
|
345
|
+
const from = parsed?.model;
|
|
346
|
+
const len = bodyLen ?? (parsed == null ? 0 : Buffer.byteLength(JSON.stringify(parsed)));
|
|
347
|
+
if (isTinyClassify(parsed, len)) {
|
|
348
|
+
const to = (typeof from === 'string' && pickClassifierModel(ids)) || from;
|
|
349
|
+
return {
|
|
350
|
+
parsed: (to && to !== from) ? { ...parsed, model: to } : parsed,
|
|
351
|
+
tiny: true,
|
|
352
|
+
from,
|
|
353
|
+
to,
|
|
354
|
+
raised: false,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (typeof from !== 'string') {
|
|
358
|
+
return { parsed, tiny: false, from, to: from, raised: false };
|
|
359
|
+
}
|
|
360
|
+
const resolved = resolveModel(from, ids);
|
|
361
|
+
const next = resolved ? { ...parsed, model: resolved } : parsed;
|
|
362
|
+
const bump = raiseReasoningMaxTokens(next);
|
|
363
|
+
return {
|
|
364
|
+
parsed: bump.parsed,
|
|
365
|
+
tiny: false,
|
|
366
|
+
from,
|
|
367
|
+
to: next.model,
|
|
368
|
+
raised: bump.raised,
|
|
369
|
+
raisedFrom: bump.from,
|
|
370
|
+
raisedTo: bump.to,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
260
374
|
/**
|
|
261
375
|
* Rewrite the model field of any request body that has one.
|
|
262
|
-
* Returns null (send as-is) or { body, from, to }. Any
|
|
263
|
-
* unreachable catalog — returns null: this layer must
|
|
264
|
-
* that would have worked without it.
|
|
376
|
+
* Returns null (send as-is) or { body, from, to, tiny?, raised? }. Any
|
|
377
|
+
* failure — bad JSON, unreachable catalog — returns null: this layer must
|
|
378
|
+
* never break a call that would have worked without it.
|
|
379
|
+
*
|
|
380
|
+
* Tiny classify is pinned here too, so OPENZOO_DEFAULT_MODEL cannot capture
|
|
381
|
+
* a 16-token yes/no even if a caller only goes through this helper.
|
|
265
382
|
*/
|
|
266
383
|
export async function maybeRewriteModel(bodyBuf) {
|
|
267
384
|
let body;
|
|
@@ -269,7 +386,15 @@ export async function maybeRewriteModel(bodyBuf) {
|
|
|
269
386
|
if (typeof body?.model !== 'string') return null;
|
|
270
387
|
let ids;
|
|
271
388
|
try { ids = await zooModelIds(); } catch { return null; }
|
|
272
|
-
const
|
|
273
|
-
if (!to) return null;
|
|
274
|
-
return {
|
|
389
|
+
const policy = rewriteChatModel(body, ids, { bodyLen: bodyBuf.length });
|
|
390
|
+
if (!policy.tiny && !policy.raised && policy.to === body.model) return null;
|
|
391
|
+
return {
|
|
392
|
+
body: Buffer.from(JSON.stringify(policy.parsed)),
|
|
393
|
+
from: body.model,
|
|
394
|
+
to: policy.parsed.model,
|
|
395
|
+
tiny: policy.tiny,
|
|
396
|
+
raised: policy.raised,
|
|
397
|
+
raisedFrom: policy.raisedFrom,
|
|
398
|
+
raisedTo: policy.raisedTo,
|
|
399
|
+
};
|
|
275
400
|
}
|