UNPKG

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.

63 lines (54 loc) 2.03 kB
/** * Reward pipeline for the LinUCB bandit (Phase 4.1). * * Combines quality score, normalised cost, and normalised latency into a * single scalar reward in [0, 100]. The bandit then rescales to [0, 1]. * * reward = quality - λ·norm_cost·100 - μ·norm_latency·100 * * Normalisation uses running min/max so we don't need to pre-compute global * scales. */ const logger = require('../logger'); const DEFAULT_LAMBDA = 0.3; const DEFAULT_MU = 0.1; class RewardPipeline { constructor({ lambda = DEFAULT_LAMBDA, mu = DEFAULT_MU } = {}) { this.lambda = lambda; this.mu = mu; this.costRange = { min: Infinity, max: -Infinity }; this.latencyRange = { min: Infinity, max: -Infinity }; } observe({ cost, latency }) { if (typeof cost === 'number' && cost >= 0) { this.costRange.min = Math.min(this.costRange.min, cost); this.costRange.max = Math.max(this.costRange.max, cost); } if (typeof latency === 'number' && latency >= 0) { this.latencyRange.min = Math.min(this.latencyRange.min, latency); this.latencyRange.max = Math.max(this.latencyRange.max, latency); } } _normalize(value, range) { if (!isFinite(range.min) || !isFinite(range.max) || range.max <= range.min) return 0; const v = Math.max(range.min, Math.min(range.max, value)); return (v - range.min) / (range.max - range.min); } /** * @param {object} obs - { quality: 0-100, cost: dollars, latency: ms } * @returns {number} reward in [0, 100] */ reward(obs) { this.observe(obs); const q = typeof obs.quality === 'number' ? obs.quality : 50; const cn = this._normalize(obs.cost ?? 0, this.costRange); const ln = this._normalize(obs.latency ?? 0, this.latencyRange); return Math.max(0, Math.min(100, q - this.lambda * cn * 100 - this.mu * ln * 100)); } } let _instance = null; function getRewardPipeline() { if (!_instance) _instance = new RewardPipeline(); return _instance; } module.exports = { RewardPipeline, getRewardPipeline };