@askalf/dario 4.8.121 → 4.8.122

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.
@@ -32,7 +32,7 @@
32
32
  * — the same ordering normalizeUpstreamIds() produces for live data.
33
33
  */
34
34
  export declare const BAKED_BASE_MODELS: readonly string[];
35
- /** The set of suspended families, resolved from env at call time. */
35
+ /** The set of suspended families, resolved from env (memoized by raw value). */
36
36
  export declare function suspendedFamilies(): Set<string>;
37
37
  /** True when `model` (any spelling) belongs to a suspended family. */
38
38
  export declare function isSuspendedModel(model: string): boolean;
@@ -67,14 +67,23 @@ export const BAKED_BASE_MODELS = [
67
67
  * to re-suspend if access is ever pulled again.
68
68
  */
69
69
  const DEFAULT_SUSPENDED_MODELS = '';
70
- /** The set of suspended families, resolved from env at call time. */
70
+ // Memoized by the raw env value (#642-audit): isSuspendedModel runs per request,
71
+ // and this rebuilt a Set from process.env every call. Keyed on the raw string so
72
+ // a runtime change to DARIO_SUSPENDED_MODELS still re-parses; the common (empty)
73
+ // value allocates the Set once.
74
+ let _suspendedCache = null;
75
+ /** The set of suspended families, resolved from env (memoized by raw value). */
71
76
  export function suspendedFamilies() {
72
77
  const raw = process.env.DARIO_SUSPENDED_MODELS ?? DEFAULT_SUSPENDED_MODELS;
73
- return new Set(raw
78
+ if (_suspendedCache && _suspendedCache.raw === raw)
79
+ return _suspendedCache.set;
80
+ const set = new Set(raw
74
81
  .split(',')
75
82
  .map((s) => s.trim())
76
83
  .filter((s) => s.length > 0)
77
84
  .map((s) => modelFamily(s) ?? s.toLowerCase()));
85
+ _suspendedCache = { raw, set };
86
+ return set;
78
87
  }
79
88
  /** True when `model` (any spelling) belongs to a suspended family. */
80
89
  export function isSuspendedModel(model) {
package/dist/proxy.js CHANGED
@@ -455,6 +455,21 @@ function sanitizeContent(text, patterns) {
455
455
  }
456
456
  return result.replace(/\n{3,}/g, '\n\n').trim();
457
457
  }
458
+ // Memoize the compiled preserve-tag pattern set by Set reference (#642-audit):
459
+ // opts.preserveOrchestrationTags is a single Set instance passed on every
460
+ // request, so recompile the ~28 patterns once instead of per request. The
461
+ // default (no-preserve) path already uses the precompiled constant.
462
+ let _orchPreserveKey;
463
+ let _orchPatterns;
464
+ function orchestrationPatternsFor(preserveTags) {
465
+ if (preserveTags === undefined)
466
+ return ORCHESTRATION_PATTERNS_DEFAULT;
467
+ if (preserveTags !== _orchPreserveKey) {
468
+ _orchPreserveKey = preserveTags;
469
+ _orchPatterns = buildOrchestrationPatterns(preserveTags);
470
+ }
471
+ return _orchPatterns;
472
+ }
458
473
  /**
459
474
  * Strip orchestration tags from all messages in a request body.
460
475
  *
@@ -465,7 +480,7 @@ export function sanitizeMessages(body, preserveTags) {
465
480
  const messages = body.messages;
466
481
  if (!messages)
467
482
  return;
468
- const patterns = preserveTags === undefined ? ORCHESTRATION_PATTERNS_DEFAULT : buildOrchestrationPatterns(preserveTags);
483
+ const patterns = orchestrationPatternsFor(preserveTags);
469
484
  for (const msg of messages) {
470
485
  if (typeof msg.content === 'string') {
471
486
  msg.content = sanitizeContent(msg.content, patterns);
@@ -1758,9 +1773,15 @@ export async function startProxy(opts = {}) {
1758
1773
  // recognizes through its own Anthropic gateway, bypassing localhost).
1759
1774
  let forcedProvider = cliProviderOverride;
1760
1775
  let requestEffort; // dario#419 — per-request effort parsed from a model-name suffix (model:high / model-high)
1776
+ // Parsed body, shared between the provider-prefix detection below and the
1777
+ // template-build block further down so the same bytes are not JSON.parsed
1778
+ // twice per request (#642-audit). Mutations in the prefix block re-serialize
1779
+ // `body` FROM this object, so it always represents the current body.
1780
+ let parsedBody = null;
1761
1781
  if (body.length > 0) {
1762
1782
  try {
1763
1783
  const parsed = JSON.parse(body.toString());
1784
+ parsedBody = parsed;
1764
1785
  const rawModel = parsed.model ?? '';
1765
1786
  const prefix = parseProviderPrefix(rawModel);
1766
1787
  if (prefix) {
@@ -1866,7 +1887,11 @@ export async function startProxy(opts = {}) {
1866
1887
  } : undefined;
1867
1888
  if (body.length > 0) {
1868
1889
  try {
1869
- const parsed = JSON.parse(body.toString());
1890
+ // Reuse the object parsed for provider-prefix detection above — the same
1891
+ // bytes, so re-parsing is wasted work on large bodies (#642-audit). Falls
1892
+ // back to a fresh parse when the earlier block didn't run (e.g. the body
1893
+ // was not JSON, or an OpenAI-backend reroute skipped it).
1894
+ const parsed = parsedBody ?? JSON.parse(body.toString());
1870
1895
  // Strip orchestration tags from messages (Aider, Cursor, etc.)
1871
1896
  sanitizeMessages(parsed, opts.preserveOrchestrationTags);
1872
1897
  const result = isOpenAI ? openaiToAnthropic(parsed, modelOverride) : (modelOverride ? { ...parsed, model: modelOverride } : parsed);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.121",
3
+ "version": "4.8.122",
4
4
  "description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
5
5
  "type": "module",
6
6
  "bin": {