lynkr
Version:
Self-hosted LLM gateway and tier-routing proxy for Claude Code, Cursor, and Codex. Routes across Ollama, AWS Bedrock, OpenRouter, Databricks, Azure OpenAI, llama.cpp, and LM Studio with prompt caching, MCP tools, and 60-80% cost savings.
131 lines (118 loc) • 3.78 kB
JavaScript
/**
* Build the kNN router index from telemetry (and optional RouterBench bootstrap).
*
* Phase 3.1 of the routing overhaul. Should be run nightly:
* node scripts/build-knn-index.js [--days 30] [--bootstrap path/to/routerbench.jsonl]
*
* RouterBench bootstrap format (one JSON per line):
* { "query": "...", "provider": "anthropic", "model": "claude-...",
* "quality": 87, "cost": 0.0034, "latency": 1200, "tier": "COMPLEX" }
*/
const fs = require('fs');
const path = require('path');
const { generateEmbedding } = require('../src/cache/embeddings');
const { getKnnRouter } = require('../src/routing/knn-router');
const DEFAULT_DAYS = 30;
const TELEMETRY_DB_CANDIDATES = [
path.join(__dirname, '../.lynkr/telemetry.db'),
path.join(__dirname, '../data/lynkr.db'),
];
function _findDb() {
for (const p of TELEMETRY_DB_CANDIDATES) if (fs.existsSync(p)) return p;
return null;
}
function _parseArgs(argv) {
const out = { days: DEFAULT_DAYS, bootstrap: null };
for (let i = 0; i < argv.length; i++) {
if (argv[i] === '--days') out.days = Number(argv[++i]) || DEFAULT_DAYS;
else if (argv[i] === '--bootstrap') out.bootstrap = argv[++i];
}
return out;
}
async function _readTelemetry(days) {
const dbPath = _findDb();
if (!dbPath) return [];
let Database;
try {
Database = require('better-sqlite3');
} catch {
console.error('better-sqlite3 not installed');
return [];
}
const db = new Database(dbPath, { readonly: true, fileMustExist: true });
try {
const since = Date.now() - days * 24 * 3600 * 1000;
return db
.prepare(
`SELECT request_text AS query, provider, model, quality_score AS quality,
cost, total_latency_ms AS latency, tier
FROM routing_telemetry
WHERE timestamp >= ?
AND quality_score IS NOT NULL
AND request_text IS NOT NULL
AND request_text != ''`
)
.all(since);
} catch (err) {
console.error(`Telemetry query failed: ${err.message}`);
return [];
} finally {
try { db.close(); } catch {}
}
}
async function _readBootstrap(filePath) {
if (!filePath || !fs.existsSync(filePath)) return [];
const lines = fs.readFileSync(filePath, 'utf8').split('\n').filter(Boolean);
const out = [];
for (const line of lines) {
try {
out.push(JSON.parse(line));
} catch {
// skip malformed
}
}
return out;
}
async function build({ days = DEFAULT_DAYS, bootstrap = null } = {}) {
const router = getKnnRouter();
if (!router.ready) {
console.error('Router index not ready (hnswlib-node may be missing). Aborting.');
process.exit(2);
}
const teleRows = await _readTelemetry(days);
const bootRows = await _readBootstrap(bootstrap);
const all = [...bootRows, ...teleRows];
console.log(`Building index from ${bootRows.length} bootstrap + ${teleRows.length} telemetry rows`);
let added = 0;
let failed = 0;
for (const row of all) {
const text = row.query || row.request_text;
if (!text) continue;
try {
const emb = await generateEmbedding(text);
router.add(emb, {
provider: row.provider,
model: row.model,
quality: row.quality,
cost: row.cost,
latency: row.latency,
tier: row.tier,
});
added++;
if (added % 100 === 0) console.log(` ${added} indexed...`);
} catch (err) {
failed++;
}
}
router.save();
console.log(`Indexed ${added}, failed ${failed}. Index size: ${router.size}`);
}
if (require.main === module) {
const opts = _parseArgs(process.argv.slice(2));
build(opts).catch((err) => {
console.error(err.stack || err.message);
process.exit(1);
});
}
module.exports = { build };