Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,23 @@ TOON_FAIL_OPEN=true
# Values: true | false
TOON_LOG_STATS=true

# DESCRIPTION: Enable GCF (Graph Compact Format) encoding for large structured payloads.
# Drop-in alternative to TOON; takes precedence over TOON when enabled. Opt-in.
# Values: true | false
GCF_ENABLED=false
# DESCRIPTION: Minimum byte size before GCF encoding kicks in.
GCF_MIN_BYTES=4096
# DESCRIPTION: Continue without GCF on encoder failure instead of erroring.
# Values: true | false
GCF_FAIL_OPEN=true
# DESCRIPTION: Log per-request GCF savings stats.
# Values: true | false
GCF_LOG_STATS=true
# DESCRIPTION: Round-trip verify each encoding and keep the original JSON on any mismatch
# (makes the compression provably lossless per payload; adds one decode per converted blob).
# Values: true | false
GCF_VERIFY=true

# DESCRIPTION: Master switch for Headroom sidecar context compression.
# Values: true | false
HEADROOM_ENABLED=true
Expand Down
14 changes: 12 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dev": "nodemon index.js",
"lint": "eslint src index.js",
"test": "npm run test:unit && npm run test:performance",
"test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com LOG_FILE_ENABLED=false node --test test/routing.test.js test/hybrid-routing-integration.test.js test/web-tools.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/toon-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/distill.test.js test/large-payload.test.js test/code-mode.test.js test/prompt-cache-injection.test.js test/risk-analyzer.test.js test/interaction-block.test.js test/preflight.test.js test/token-reduction.test.js test/session-affinity.test.js test/model-registry-cost.test.js test/task-decomposition.test.js test/output-format-guard.test.js test/tier-fallback.test.js test/wrap.test.js test/init.test.js test/agent-learning.test.js test/tool-call-response-metadata.test.js test/degradation.test.js test/routing-telemetry-columns.test.js test/sticky-routing.test.js test/knn-ambiguous-escalate.test.js test/deescalator.test.js test/client-profiles.test.js test/strip-internal-fields.test.js test/complexity-tool-subtraction.test.js test/bandit.test.js test/routing-propensity.test.js test/reward-pipeline.test.js test/knn-cold-start.test.js test/calibration.test.js test/feedback-loop.test.js test/session-fingerprint.test.js test/side-request-guards.test.js test/verifier.test.js test/intent-score.test.js",
"test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com LOG_FILE_ENABLED=false node --test test/routing.test.js test/hybrid-routing-integration.test.js test/web-tools.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/toon-compression.test.js test/gcf-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/distill.test.js test/large-payload.test.js test/code-mode.test.js test/prompt-cache-injection.test.js test/risk-analyzer.test.js test/interaction-block.test.js test/preflight.test.js test/token-reduction.test.js test/session-affinity.test.js test/model-registry-cost.test.js test/task-decomposition.test.js test/output-format-guard.test.js test/tier-fallback.test.js test/wrap.test.js test/init.test.js test/agent-learning.test.js test/tool-call-response-metadata.test.js test/degradation.test.js test/routing-telemetry-columns.test.js test/sticky-routing.test.js test/knn-ambiguous-escalate.test.js test/deescalator.test.js test/client-profiles.test.js test/strip-internal-fields.test.js test/complexity-tool-subtraction.test.js test/bandit.test.js test/routing-propensity.test.js test/reward-pipeline.test.js test/knn-cold-start.test.js test/calibration.test.js test/feedback-loop.test.js test/session-fingerprint.test.js test/side-request-guards.test.js test/verifier.test.js test/intent-score.test.js",
"test:memory": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js",
"test:new-features": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js",
"test:performance": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/hybrid-routing-performance.test.js && DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js",
Expand Down Expand Up @@ -71,6 +71,7 @@
"@azure/openai": "^2.0.0",
"@babel/parser": "^7.29.0",
"@babel/traverse": "^7.29.0",
"@blackwell-systems/gcf": "2.4.0",
"@toon-format/toon": "^2.1.0",
"cockatiel": "^3.2.1",
"compression": "^1.7.4",
Expand Down
20 changes: 20 additions & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ const toonMinBytes = Number.parseInt(process.env.TOON_MIN_BYTES ?? "4096", 10);
const toonFailOpen = process.env.TOON_FAIL_OPEN !== "false"; // default true
const toonLogStats = process.env.TOON_LOG_STATS !== "false"; // default true

// GCF payload compression (opt-in; drop-in alternative to TOON)
const gcfEnabled = process.env.GCF_ENABLED === "true"; // default false
const gcfMinBytes = Number.parseInt(process.env.GCF_MIN_BYTES ?? "4096", 10);
const gcfFailOpen = process.env.GCF_FAIL_OPEN !== "false"; // default true
const gcfLogStats = process.env.GCF_LOG_STATS !== "false"; // default true
const gcfVerify = process.env.GCF_VERIFY !== "false"; // default true (round-trip check)

// Smart tool selection configuration (always enabled)
const smartToolSelectionMode = (process.env.SMART_TOOL_SELECTION_MODE ?? "heuristic").toLowerCase();
const smartToolSelectionTokenBudget = Number.parseInt(
Expand Down Expand Up @@ -867,6 +874,13 @@ var config = {
failOpen: toonFailOpen,
logStats: toonLogStats,
},
gcf: {
enabled: gcfEnabled,
minBytes: Number.isNaN(gcfMinBytes) ? 4096 : gcfMinBytes,
failOpen: gcfFailOpen,
logStats: gcfLogStats,
verify: gcfVerify,
},
smartToolSelection: {
enabled: true, // HARDCODED - always enabled
mode: smartToolSelectionMode,
Expand Down Expand Up @@ -1055,6 +1069,12 @@ function reloadConfig() {
config.toon.minBytes = Number.isNaN(newToonMinBytes) ? 4096 : newToonMinBytes;
config.toon.failOpen = process.env.TOON_FAIL_OPEN !== "false";
config.toon.logStats = process.env.TOON_LOG_STATS !== "false";
config.gcf.enabled = process.env.GCF_ENABLED === "true";
const newGcfMinBytes = Number.parseInt(process.env.GCF_MIN_BYTES ?? "4096", 10);
config.gcf.minBytes = Number.isNaN(newGcfMinBytes) ? 4096 : newGcfMinBytes;
config.gcf.failOpen = process.env.GCF_FAIL_OPEN !== "false";
config.gcf.logStats = process.env.GCF_LOG_STATS !== "false";
config.gcf.verify = process.env.GCF_VERIFY !== "false";

// Tier routing (critical for fixing model name issues without restart)
config.modelTiers.SIMPLE = process.env.TIER_SIMPLE?.trim() || null;
Expand Down
275 changes: 275 additions & 0 deletions src/context/gcf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
const logger = require("../logger");
const { countTokens } = require("../routing/tokenizer");

// GCF (Graph Compact Format) context compression. Drop-in alternative to the
// TOON adapter (src/context/toon.js): same encode-only, fail-open,
// read-only-context contract; only the encoder differs. Round-trips losslessly.
//
// @blackwell-systems/gcf ships a CommonJS build, so the encoder is resolved with
// a synchronous require and cached.

let cachedEncode;
let cachedDecode;
let cachedLoadError;
let warnedMissingDependency = false;

// A clear byte reduction reliably implies a token reduction, so blobs that shrink by
// at least this margin convert without paying to tokenize both strings. Blobs in the
// ambiguous zone (or larger) fall through to the exact token comparison.
const BYTE_FASTPATH_RATIO = 0.9;

function normaliseSettings(settings = {}) {
const minBytesRaw =
typeof settings.minBytes === "number" ? settings.minBytes : Number.parseInt(settings.minBytes ?? "4096", 10);
return {
enabled: settings.enabled === true,
minBytes: Number.isFinite(minBytesRaw) && minBytesRaw > 0 ? minBytesRaw : 4096,
failOpen: settings.failOpen !== false,
logStats: settings.logStats !== false,
verify: settings.verify !== false,
};
}

// Order-insensitive for object keys, order-sensitive for arrays: the round-trip
// check treats two JSON values as equal when they carry the same data.
function deepEqual(a, b) {
if (a === b) return true;
if (a === null || b === null || typeof a !== typeof b) return false;
if (Array.isArray(a)) {
if (!Array.isArray(b) || a.length !== b.length) return false;
return a.every((v, i) => deepEqual(v, b[i]));
}
if (typeof a === "object") {
const ka = Object.keys(a);
const kb = Object.keys(b);
if (ka.length !== kb.length) return false;
return ka.every((k) => Object.prototype.hasOwnProperty.call(b, k) && deepEqual(a[k], b[k]));
}
return false;
}

function resolveEncodeFn(overrideEncode) {
if (typeof overrideEncode === "function") return overrideEncode;
if (cachedEncode !== undefined) return cachedEncode;
try {
const mod = require("@blackwell-systems/gcf");
const fn = mod?.encodeGeneric ?? mod?.default?.encodeGeneric ?? null;
cachedEncode = typeof fn === "function" ? fn : null;
cachedLoadError = cachedEncode
? null
: new Error("Missing encodeGeneric() export from @blackwell-systems/gcf");
} catch (err) {
cachedEncode = null;
cachedLoadError = err;
}
return cachedEncode;
}

function resolveDecodeFn(overrideDecode) {
if (typeof overrideDecode === "function") return overrideDecode;
if (cachedDecode !== undefined) return cachedDecode;
try {
const mod = require("@blackwell-systems/gcf");
const fn = mod?.decodeGeneric ?? mod?.default?.decodeGeneric ?? null;
cachedDecode = typeof fn === "function" ? fn : null;
} catch {
cachedDecode = null;
}
return cachedDecode;
}

function looksLikeJsonObjectOrArray(text) {
if (typeof text !== "string") return false;
const trimmed = text.trim();
if (trimmed.length < 2) return false;
return (
(trimmed.startsWith("{") && trimmed.endsWith("}")) ||
(trimmed.startsWith("[") && trimmed.endsWith("]"))
);
}

function safeJsonParse(text) {
try {
return JSON.parse(text);
} catch {
return null;
}
}

function toGcfString(encodeFn, value) {
const encoded = encodeFn(value);
if (typeof encoded === "string") return encoded;
if (encoded && typeof encoded[Symbol.iterator] === "function") {
return Array.from(encoded).join("\n");
}
return "";
}

function compressStringContent(content, cfg, encodeFn, decodeFn, stats, model) {
if (typeof content !== "string") return content;

const originalBytes = Buffer.byteLength(content, "utf8");
if (originalBytes < cfg.minBytes) {
stats.skippedBySize += 1;
return content;
}

stats.candidateCount += 1;
if (!looksLikeJsonObjectOrArray(content)) {
stats.skippedByShape += 1;
return content;
}

const parsed = safeJsonParse(content);
if (!parsed || typeof parsed !== "object") {
stats.skippedByParse += 1;
return content;
}

const gcfText = toGcfString(encodeFn, parsed);
if (typeof gcfText !== "string" || gcfText.trim().length === 0) {
return content;
}

const gcfBytes = Buffer.byteLength(gcfText, "utf8");

// Fast path: a clear byte reduction reliably means a token reduction too, so skip
// the (per-blob, hot-path) token comparison.
if (gcfBytes <= originalBytes * BYTE_FASTPATH_RATIO) {
if (!verifiesLossless(parsed, gcfText, cfg, decodeFn, stats)) return content;
stats.convertedCount += 1;
stats.originalBytes += originalBytes;
stats.compressedBytes += gcfBytes;
return gcfText;
}

// Ambiguous zone (or larger in bytes): compare token counts with the target model's
// encoding, and keep the original JSON if GCF does not reduce the token count.
const originalTokens = countTokens(content, model);
const gcfTokens = countTokens(gcfText, model);
if (gcfTokens >= originalTokens) {
stats.skippedByGrowth += 1;
return content;
}

if (!verifiesLossless(parsed, gcfText, cfg, decodeFn, stats)) return content;
stats.convertedCount += 1;
stats.originalBytes += originalBytes;
stats.compressedBytes += gcfBytes;
stats.originalTokens += originalTokens;
stats.compressedTokens += gcfTokens;
return gcfText;
}

// Round-trip check on a payload we have otherwise decided to convert: decode the
// encoding and require that it reproduces the input exactly. GCF is lossless by
// design, so this is insurance rather than an expected path; it makes the
// compression provably lossless per payload. Returns true (safe to convert) when
// verification is off or the decoder is unavailable.
function verifiesLossless(parsed, gcfText, cfg, decodeFn, stats) {
if (!cfg.verify || typeof decodeFn !== "function") return true;
let decoded;
try {
decoded = decodeFn(gcfText);
} catch {
decoded = undefined;
}
if (deepEqual(parsed, decoded)) return true;
stats.skippedByVerify += 1;
return false;
}

function applyGcfCompression(payload, settings = {}, options = {}) {
const cfg = normaliseSettings(settings);
const stats = {
enabled: cfg.enabled,
available: true,
convertedCount: 0,
candidateCount: 0,
skippedBySize: 0,
skippedByShape: 0,
skippedByParse: 0,
skippedByGrowth: 0,
skippedByVerify: 0,
failureCount: 0,
originalBytes: 0,
compressedBytes: 0,
originalTokens: 0,
compressedTokens: 0,
};

if (!cfg.enabled) return { payload, stats };
if (!payload || !Array.isArray(payload.messages) || payload.messages.length === 0) {
return { payload, stats };
}

// Token counts for the never-grow guard use the target model's encoding when known.
const model = typeof payload.model === "string" ? payload.model : null;

const encodeFn = resolveEncodeFn(options.encode);
if (typeof encodeFn !== "function") {
stats.available = false;
const err = cachedLoadError ?? new Error("GCF encoder unavailable");
if (!cfg.failOpen) throw err;
if (!warnedMissingDependency) {
logger.warn(
{ error: err.message },
"GCF enabled but encoder dependency is unavailable; falling back to JSON",
);
warnedMissingDependency = true;
}
return { payload, stats };
}

const decodeFn = cfg.verify ? resolveDecodeFn(options.decode) : null;

for (const message of payload.messages) {
if (!message || typeof message !== "object") continue;
if (message.role === "tool") continue; // Never mutate machine-executed protocol payloads
try {
if (typeof message.content === "string") {
message.content = compressStringContent(message.content, cfg, encodeFn, decodeFn, stats, model);
continue;
}

if (!Array.isArray(message.content)) continue;
for (const block of message.content) {
if (!block || typeof block !== "object") continue;

// Keep protocol blocks untouched. Only compress user-language text fields.
if (block.type === "text" && typeof block.text === "string") {
block.text = compressStringContent(block.text, cfg, encodeFn, decodeFn, stats, model);
continue;
}

if (block.type === "input_text" && typeof block.input_text === "string") {
block.input_text = compressStringContent(block.input_text, cfg, encodeFn, decodeFn, stats, model);
}
}
} catch (err) {
stats.failureCount += 1;
if (!cfg.failOpen) throw err;
}
}

if (cfg.logStats && (stats.convertedCount > 0 || stats.skippedByVerify > 0)) {
logger.info(
{
convertedCount: stats.convertedCount,
candidateCount: stats.candidateCount,
skippedByVerify: stats.skippedByVerify,
originalBytes: stats.originalBytes,
compressedBytes: stats.compressedBytes,
originalTokens: stats.originalTokens,
compressedTokens: stats.compressedTokens,
},
"GCF compression applied to message context",
);
}

return { payload, stats };
}

module.exports = {
applyGcfCompression,
};
Loading
Loading