UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

217 lines (216 loc) 6.13 kB
/** * Sampling Strategies * Control which spans are exported for production optimization */ import { SpanStatus } from "../../types/index.js"; /** * Always sample all spans */ export class AlwaysSampler { name = "always"; shouldSample(_span) { return true; } getDescription() { return "Samples 100% of spans"; } } /** * Never sample any spans */ export class NeverSampler { name = "never"; shouldSample(_span) { return false; } getDescription() { return "Samples 0% of spans"; } } /** * Sample spans based on a probability ratio */ export class RatioSampler { name = "ratio"; ratio; constructor(ratio) { if (ratio < 0 || ratio > 1) { throw new Error("Ratio must be between 0 and 1"); } this.ratio = ratio; } shouldSample(_span) { return Math.random() < this.ratio; } getDescription() { return `Samples ${this.ratio * 100}% of spans`; } } /** * Sample based on trace ID for consistent sampling across a trace */ export class TraceIdRatioSampler { name = "trace-id-ratio"; ratio; upperBound; constructor(ratio) { if (ratio < 0 || ratio > 1) { throw new Error("Ratio must be between 0 and 1"); } this.ratio = ratio; this.upperBound = Math.floor(ratio * 0xffffffff); } shouldSample(span) { // Use first 8 chars of trace ID as hash const hash = parseInt(span.traceId.substring(0, 8), 16); return !isNaN(hash) && hash < this.upperBound; } getDescription() { return `Samples ${this.ratio * 100}% of traces (consistent per trace)`; } } /** * Sample based on span attributes (e.g., errors, specific providers) */ export class AttributeBasedSampler { name = "attribute-based"; rules; defaultSampler; constructor(rules, defaultSampler = new RatioSampler(0.1)) { // Sort rules by priority (higher first) this.rules = [...rules].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); this.defaultSampler = defaultSampler; } shouldSample(span) { for (const rule of this.rules) { if (this.matchesRule(span, rule)) { return rule.sample; } } return this.defaultSampler.shouldSample(span); } matchesRule(span, rule) { for (const [key, value] of Object.entries(rule.conditions)) { const spanValue = span.attributes[key]; // Support wildcard matching if (value === "*" && spanValue !== undefined) { continue; } if (spanValue !== value) { return false; } } return true; } getDescription() { return `Attribute-based sampling with ${this.rules.length} rules`; } } /** * Priority-based sampler - always sample high-priority spans */ export class PrioritySampler { name = "priority"; highPriorityTypes; fallbackSampler; constructor(highPriorityTypes = ["model.generation", "tool.call"], fallbackSampler = new RatioSampler(0.1)) { this.highPriorityTypes = highPriorityTypes; this.fallbackSampler = fallbackSampler; } shouldSample(span) { // Always sample errors if (span.status === SpanStatus.ERROR) { return true; } // Always sample high-priority span types if (this.highPriorityTypes.includes(span.type)) { return true; } return this.fallbackSampler.shouldSample(span); } getDescription() { return `Priority sampling for ${this.highPriorityTypes.join(", ")} spans`; } } /** * Error-only sampler - only sample error spans */ export class ErrorOnlySampler { name = "error-only"; shouldSample(span) { return span.status === SpanStatus.ERROR; } getDescription() { return "Samples only error spans"; } } /** * Composite sampler that combines multiple samplers */ export class CompositeSampler { name = "composite"; samplers; totalWeight; constructor(samplers) { this.samplers = samplers; this.totalWeight = samplers.reduce((sum, s) => sum + s.weight, 0); } shouldSample(span) { let random = Math.random() * this.totalWeight; for (const { sampler, weight } of this.samplers) { random -= weight; if (random <= 0) { return sampler.shouldSample(span); } } return this.samplers[this.samplers.length - 1].sampler.shouldSample(span); } getDescription() { return `Composite of ${this.samplers.length} samplers`; } } /** * Custom sampler that uses a user-provided function */ export class CustomSampler { name = "custom"; sampleFn; description; constructor(sampleFn, description = "Custom sampling function") { this.sampleFn = sampleFn; this.description = description; } shouldSample(span) { return this.sampleFn(span); } getDescription() { return this.description; } } /** * Factory for creating samplers from configuration */ export class SamplerFactory { static create(config) { switch (config.type) { case "always": return new AlwaysSampler(); case "never": return new NeverSampler(); case "ratio": return new RatioSampler(config.ratio ?? 0.1); case "trace-id-ratio": return new TraceIdRatioSampler(config.ratio ?? 0.1); case "attribute-based": return new AttributeBasedSampler(config.rules ?? [], config.defaultRatio !== undefined ? new RatioSampler(config.defaultRatio) : undefined); case "priority": return new PrioritySampler(); case "error-only": return new ErrorOnlySampler(); default: return new RatioSampler(0.1); } } }