@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
JavaScript
/**
* 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);
}
}
}