Skip to main content

Analytics Reference

NeuroLink provides comprehensive analytics capabilities for tracking token usage, costs, performance metrics, and quality evaluation across all AI provider interactions.

Overview

The analytics system in NeuroLink consists of several interconnected components:

ComponentPurpose
Token Usage TrackingMonitor input/output tokens, cache tokens, and reasoning tokens
Cost AnalyticsEstimate and track costs across providers and models
Performance MetricsMeasure response times, throughput, and memory usage
Quality EvaluationAssess response relevance, accuracy, and completeness
Middleware IntegrationAutomatic analytics collection via middleware

Token Usage Tracking

Basic Token Usage

NeuroLink automatically tracks token usage for every generation:

import { NeuroLink } from "@juspay/neurolink";

const neurolink = new NeuroLink();

const result = await neurolink.generate({
input: { text: "Explain quantum computing in simple terms" },
provider: "openai",
enableAnalytics: true,
});

// Access token usage
console.log("Token Usage:", {
input: result.usage?.input,
output: result.usage?.output,
total: result.usage?.total,
});

// Full analytics data
console.log("Analytics:", result.analytics);

TokenUsage Type

The TokenUsage type provides detailed token information:

type TokenUsage = {
/** Number of input/prompt tokens */
input: number;
/** Number of output/completion tokens */
output: number;
/** Total tokens (input + output) */
total: number;
/** Tokens used to create cache entries (Anthropic, Google) */
cacheCreationTokens?: number;
/** Tokens read from cache (cost savings) */
cacheReadTokens?: number;
/** Tokens used for reasoning/thinking (o1, Claude thinking) */
reasoning?: number;
/** Percentage of cost saved through caching */
cacheSavingsPercent?: number;
};

Cache Token Tracking

For providers that support prompt caching (Anthropic, Google), NeuroLink tracks cache metrics:

const result = await neurolink.generate({
input: { text: "Analyze this document..." },
provider: "anthropic",
enableAnalytics: true,
});

if (result.analytics?.tokenUsage) {
const { cacheCreationTokens, cacheReadTokens, cacheSavingsPercent } =
result.analytics.tokenUsage;

if (cacheCreationTokens) {
console.log(`Cache created: ${cacheCreationTokens} tokens`);
}
if (cacheReadTokens) {
console.log(`Cache hit: ${cacheReadTokens} tokens`);
console.log(`Cost savings: ${cacheSavingsPercent}%`);
}
}

Reasoning Token Tracking

For models with extended thinking capabilities (OpenAI o1, Anthropic Claude with thinking, Gemini 3):

const result = await neurolink.generate({
input: { text: "Solve this complex mathematical proof..." },
provider: "openai",
model: "o1-mini",
enableAnalytics: true,
});

if (result.analytics?.tokenUsage.reasoning) {
console.log(
`Reasoning tokens used: ${result.analytics.tokenUsage.reasoning}`,
);
}

Cost Analytics

Automatic Cost Estimation

NeuroLink automatically estimates costs based on provider pricing:

const result = await neurolink.generate({
input: { text: "Write a detailed business plan" },
provider: "openai",
model: "gpt-4o",
enableAnalytics: true,
});

if (result.analytics?.cost !== undefined) {
console.log(`Estimated cost: $${result.analytics.cost.toFixed(5)}`);
}

Cost Calculation Formula

Costs are calculated using per-token pricing:

// Internal cost calculation
const inputCost = (tokens.input / 1000) * costInfo.input;
const outputCost = (tokens.output / 1000) * costInfo.output;
const totalCost = inputCost + outputCost;

Provider Pricing Configuration

NeuroLink uses configurable pricing for each provider:

ProviderDefault Input Cost (per 1K)Default Output Cost (per 1K)
OpenAI$0.00015$0.0006
Anthropic$0.0015$0.0075
Google AI$0.000075$0.0003
Google Vertex$0.000075$0.0003
Bedrock$0.0015$0.0075
Azure$0.00015$0.0006
Mistral$0.0001$0.0003
HuggingFace$0.0002$0.0008
Ollama$0$0

Custom Cost Configuration

Override default pricing via environment variables:

# Custom pricing for Google AI
GOOGLE_AI_DEFAULT_INPUT_COST=0.0001
GOOGLE_AI_DEFAULT_OUTPUT_COST=0.0004

# Custom pricing for OpenAI
OPENAI_DEFAULT_INPUT_COST=0.0002
OPENAI_DEFAULT_OUTPUT_COST=0.0008

Aggregating Costs

Track cumulative costs across multiple requests:

import { NeuroLink } from "@juspay/neurolink";

const neurolink = new NeuroLink();
const usages = [];

// Collect usage from multiple requests
for (const prompt of prompts) {
const result = await neurolink.generate({
input: { text: prompt },
enableAnalytics: true,
});
if (result.usage) {
usages.push(result.usage);
}
}

// Calculate total usage manually
const totalUsage = usages.reduce(
(total, current) => ({
input: total.input + current.input,
output: total.output + current.output,
total: total.total + current.total,
}),
{ input: 0, output: 0, total: 0 },
);

console.log(`Total tokens used: ${totalUsage.total}`);

Performance Metrics

Response Time Tracking

Every request automatically tracks response time:

const result = await neurolink.generate({
input: { text: "Quick response test" },
enableAnalytics: true,
});

console.log(`Response time: ${result.responseTime}ms`);
console.log(`Analytics duration: ${result.analytics?.requestDuration}ms`);

AnalyticsData Structure

The complete analytics data structure:

type AnalyticsData = {
/** Provider used for the request */
provider: string;
/** Model used for the request */
model?: string;
/** Token usage breakdown */
tokenUsage: TokenUsage;
/** Request duration in milliseconds */
requestDuration: number;
/** ISO timestamp of the request */
timestamp: string;
/** Estimated cost in USD */
cost?: number;
/** Custom context data */
context?: Record<string, unknown>;
};

Performance Metrics Type

For advanced performance tracking:

type PerformanceMetrics = {
/** Start timestamp */
startTime: number;
/** End timestamp */
endTime?: number;
/** Total duration in ms */
duration?: number;
/** Memory usage at start */
memoryStart: NodeJS.MemoryUsage;
/** Memory usage at end */
memoryEnd?: NodeJS.MemoryUsage;
/** Memory delta */
memoryDelta?: {
rss: number;
heapTotal: number;
heapUsed: number;
external: number;
};
};

Stream Performance Metrics

For streaming requests, additional metrics are available:

type StreamAnalyticsData = {
/** Tool execution results with timing */
toolResults?: Promise<Array<unknown>>;
/** Tool calls made during stream */
toolCalls?: Promise<Array<unknown>>;
/** Stream performance metrics */
performance?: {
startTime: number;
endTime?: number;
chunkCount: number;
avgChunkSize: number;
totalBytes: number;
};
/** Provider analytics */
providerAnalytics?: AnalyticsData;
};

Streaming Example

const stream = await neurolink.stream({
input: { text: "Write a long story" },
enableAnalytics: true,
});

let chunkCount = 0;
for await (const chunk of stream.textStream) {
chunkCount++;
process.stdout.write(chunk);
}

// Access stream analytics after completion
const analytics = await stream.analytics;
console.log(`\nChunks received: ${chunkCount}`);
console.log(`Total tokens: ${analytics?.tokenUsage?.total}`);

Quality Evaluation

Enabling Evaluation

NeuroLink can automatically evaluate response quality:

const result = await neurolink.generate({
input: { text: "Explain machine learning" },
provider: "openai",
enableAnalytics: true,
enableEvaluation: true,
});

if (result.evaluation) {
console.log("Evaluation Results:", {
relevance: result.evaluation.relevance,
accuracy: result.evaluation.accuracy,
completeness: result.evaluation.completeness,
overall: result.evaluation.overall,
reasoning: result.evaluation.reasoning,
});
}

EvaluationData Structure

type EvaluationData = {
// Core scores (1-10 scale)
/** How well response addresses query intent */
relevance: number;
/** Factual correctness and accuracy */
accuracy: number;
/** How completely the response addresses the query */
completeness: number;
/** Overall quality score */
overall: number;

// Domain-specific scores
/** Domain alignment score */
domainAlignment?: number;
/** Terminology accuracy */
terminologyAccuracy?: number;
/** Tool effectiveness score */
toolEffectiveness?: number;

// Quality indicators
/** True if response deviates from query/domain */
isOffTopic: boolean;
/** Quality alert level: low, medium, high, none */
alertSeverity: "low" | "medium" | "high" | "none";
/** Brief justification for scores */
reasoning: string;
/** Suggestions for improvement */
suggestedImprovements?: string;

// Metadata
/** Model used for evaluation */
evaluationModel: string;
/** Time taken for evaluation (ms) */
evaluationTime: number;
/** Domain for evaluation */
evaluationDomain?: string;
};

Domain-Aware Evaluation

Configure evaluation for specific domains:

const result = await neurolink.generate({
input: { text: "What are the side effects of aspirin?" },
provider: "openai",
enableEvaluation: true,
evaluationDomain: "healthcare",
});

if (result.evaluation?.domainEvaluation) {
console.log("Domain Evaluation:", {
domainRelevance: result.evaluation.domainEvaluation.domainRelevance,
terminologyAccuracy: result.evaluation.domainEvaluation.terminologyAccuracy,
domainExpertise: result.evaluation.domainEvaluation.domainExpertise,
});
}

Evaluation Providers

Evaluation can use different providers:

type EvaluationProvider =
| "openai"
| "anthropic"
| "vertex"
| "google-ai"
| "local";

Analytics Middleware

Using Analytics Middleware

NeuroLink provides built-in analytics middleware:

import { createAnalyticsMiddleware } from "@juspay/neurolink/middleware";

const analyticsMiddleware = createAnalyticsMiddleware();

const neurolink = new NeuroLink({
middleware: [analyticsMiddleware],
});

Middleware Metadata

The analytics middleware provides:

const metadata = {
id: "analytics",
name: "Analytics Tracking",
description:
"Tracks token usage, response times, and model performance metrics",
priority: 100, // High priority to ensure capture
defaultEnabled: true,
};

Custom Analytics Collection

Implement custom analytics collection:

import type { NeuroLinkMiddleware } from "@juspay/neurolink";

function createCustomAnalyticsMiddleware(): NeuroLinkMiddleware {
const metrics: Map<string, Record<string, unknown>> = new Map();

return {
metadata: {
id: "custom-analytics",
name: "Custom Analytics",
description: "Custom analytics tracking",
priority: 90,
defaultEnabled: true,
},

wrapGenerate: async ({ doGenerate, params }) => {
const requestId = `req-${Date.now()}`;
const startTime = Date.now();

try {
const result = await doGenerate();
const duration = Date.now() - startTime;

metrics.set(requestId, {
duration,
tokens: result.usage,
timestamp: new Date().toISOString(),
});

return result;
} catch (error) {
metrics.set(requestId, {
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - startTime,
});
throw error;
}
},
};
}

Analytics Utilities

Formatting Utilities

import {
formatTokenUsage,
formatAnalyticsForDisplay,
getAnalyticsSummary,
} from "@juspay/neurolink/utils/analyticsUtils";

// Format token usage as string
const usageString = formatTokenUsage(result.usage);
// Output: "100 input / 50 output / 20 cache-read"

// Format full analytics for display
const display = formatAnalyticsForDisplay(result.analytics);
// Output: "Provider: openai | Model: gpt-4o | Tokens: 100 input / 50 output | Cost: $0.00015 | Time: 1.2s"

// Get analytics summary
const summary = getAnalyticsSummary(result.analytics);
console.log({
totalTokens: summary.totalTokens,
costPerToken: summary.costPerToken,
requestsPerSecond: summary.requestsPerSecond,
});

Validation Utilities

import {
hasValidTokenUsage,
isTokenUsage,
} from "@juspay/neurolink/utils/analyticsUtils";

// Check if analytics has valid token usage
if (hasValidTokenUsage(result.analytics)) {
// Safe to access token fields
}

// Type guard for token usage
if (isTokenUsage(data)) {
console.log(data.total);
}

Integration with Observability Tools

OpenTelemetry Integration

Export analytics to OpenTelemetry:

import { trace, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("neurolink");

async function trackedGenerate(options: GenerateOptions) {
return tracer.startActiveSpan("neurolink.generate", async (span) => {
try {
const result = await neurolink.generate({
...options,
enableAnalytics: true,
});

// Add analytics as span attributes
if (result.analytics) {
span.setAttributes({
"ai.provider": result.analytics.provider,
"ai.model": result.analytics.model || "unknown",
"ai.tokens.input": result.analytics.tokenUsage.input,
"ai.tokens.output": result.analytics.tokenUsage.output,
"ai.tokens.total": result.analytics.tokenUsage.total,
"ai.cost": result.analytics.cost || 0,
"ai.duration_ms": result.analytics.requestDuration,
});
}

span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error),
});
throw error;
} finally {
span.end();
}
});
}

Prometheus Metrics

Export metrics to Prometheus:

import { Counter, Histogram, Gauge } from "prom-client";

// Define metrics
const tokenCounter = new Counter({
name: "neurolink_tokens_total",
help: "Total tokens used",
labelNames: ["provider", "model", "type"],
});

const costGauge = new Gauge({
name: "neurolink_cost_dollars",
help: "Estimated cost in dollars",
labelNames: ["provider", "model"],
});

const latencyHistogram = new Histogram({
name: "neurolink_request_duration_ms",
help: "Request duration in milliseconds",
labelNames: ["provider", "model"],
buckets: [100, 250, 500, 1000, 2500, 5000, 10000],
});

// Record metrics after each request
function recordMetrics(analytics: AnalyticsData) {
const labels = {
provider: analytics.provider,
model: analytics.model || "unknown",
};

tokenCounter.inc({ ...labels, type: "input" }, analytics.tokenUsage.input);
tokenCounter.inc({ ...labels, type: "output" }, analytics.tokenUsage.output);

if (analytics.cost !== undefined) {
costGauge.set(labels, analytics.cost);
}

latencyHistogram.observe(labels, analytics.requestDuration);
}

DataDog Integration

Send analytics to DataDog:

import { DogStatsDClient } from "hot-shots";

const dogstatsd = new DogStatsDClient();

function sendToDataDog(analytics: AnalyticsData) {
const tags = [
`provider:${analytics.provider}`,
`model:${analytics.model || "unknown"}`,
];

dogstatsd.increment("neurolink.requests", 1, tags);
dogstatsd.gauge("neurolink.tokens.input", analytics.tokenUsage.input, tags);
dogstatsd.gauge("neurolink.tokens.output", analytics.tokenUsage.output, tags);
dogstatsd.histogram("neurolink.latency", analytics.requestDuration, tags);

if (analytics.cost !== undefined) {
dogstatsd.gauge("neurolink.cost", analytics.cost, tags);
}
}

Custom Logging

Structured logging with analytics:

import pino from "pino";

const logger = pino({
level: "info",
formatters: {
level: (label) => ({ level: label }),
},
});

async function loggedGenerate(options: GenerateOptions) {
const result = await neurolink.generate({
...options,
enableAnalytics: true,
});

logger.info(
{
provider: result.analytics?.provider,
model: result.analytics?.model,
tokens: {
input: result.analytics?.tokenUsage.input,
output: result.analytics?.tokenUsage.output,
total: result.analytics?.tokenUsage.total,
},
cost: result.analytics?.cost,
duration: result.analytics?.requestDuration,
timestamp: result.analytics?.timestamp,
},
"AI generation completed",
);

return result;
}

Usage Statistics

Tracking Usage Over Time

Build usage dashboards with aggregated statistics:

type UsageStats = {
totalRequests: number;
totalTokens: number;
totalCost: number;
averageLatency: number;
byProvider: Map<
string,
{
requests: number;
tokens: number;
cost: number;
}
>;
byModel: Map<
string,
{
requests: number;
tokens: number;
cost: number;
}
>;
};

class UsageTracker {
private stats: UsageStats = {
totalRequests: 0,
totalTokens: 0,
totalCost: 0,
averageLatency: 0,
byProvider: new Map(),
byModel: new Map(),
};
private latencies: number[] = [];

record(analytics: AnalyticsData) {
this.stats.totalRequests++;
this.stats.totalTokens += analytics.tokenUsage.total;
this.stats.totalCost += analytics.cost || 0;
this.latencies.push(analytics.requestDuration);
this.stats.averageLatency =
this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length;

// Track by provider
const providerStats = this.stats.byProvider.get(analytics.provider) || {
requests: 0,
tokens: 0,
cost: 0,
};
providerStats.requests++;
providerStats.tokens += analytics.tokenUsage.total;
providerStats.cost += analytics.cost || 0;
this.stats.byProvider.set(analytics.provider, providerStats);

// Track by model
if (analytics.model) {
const modelStats = this.stats.byModel.get(analytics.model) || {
requests: 0,
tokens: 0,
cost: 0,
};
modelStats.requests++;
modelStats.tokens += analytics.tokenUsage.total;
modelStats.cost += analytics.cost || 0;
this.stats.byModel.set(analytics.model, modelStats);
}
}

getStats(): UsageStats {
return { ...this.stats };
}

getSummary(): string {
return `
Total Requests: ${this.stats.totalRequests}
Total Tokens: ${this.stats.totalTokens.toLocaleString()}
Total Cost: $${this.stats.totalCost.toFixed(4)}
Average Latency: ${this.stats.averageLatency.toFixed(0)}ms
`;
}
}

Rate Limiting Based on Usage

Implement rate limiting using analytics:

class UsageRateLimiter {
private tokenBudget: number;
private costBudget: number;
private usedTokens = 0;
private usedCost = 0;
private resetInterval: NodeJS.Timeout;

constructor(
options: {
tokenBudget?: number;
costBudget?: number;
resetIntervalMs?: number;
} = {},
) {
this.tokenBudget = options.tokenBudget || 1_000_000;
this.costBudget = options.costBudget || 10;

// Reset budgets periodically
this.resetInterval = setInterval(() => {
this.usedTokens = 0;
this.usedCost = 0;
}, options.resetIntervalMs || 3600000); // 1 hour default
}

canProceed(estimatedTokens: number): boolean {
return this.usedTokens + estimatedTokens <= this.tokenBudget;
}

record(analytics: AnalyticsData) {
this.usedTokens += analytics.tokenUsage.total;
this.usedCost += analytics.cost || 0;
}

getRemainingBudget(): { tokens: number; cost: number } {
return {
tokens: this.tokenBudget - this.usedTokens,
cost: this.costBudget - this.usedCost,
};
}

destroy() {
clearInterval(this.resetInterval);
}
}

CLI Analytics

Viewing Analytics in CLI

# Generate with analytics enabled
neurolink generate "Hello world" --enableAnalytics

# Output includes analytics summary
# Tokens: 15 input / 25 output / 40 total
# Cost: $0.00003
# Time: 1.2s

Verbose Analytics

# Detailed analytics output
neurolink generate "Explain AI" --enableAnalytics --verbose

# Shows full analytics breakdown including:
# - Token usage by type
# - Cost breakdown
# - Response time
# - Provider/model info

Best Practices

1. Always Enable Analytics in Production

const neurolink = new NeuroLink({
// Default analytics enabled for all requests
defaultOptions: {
enableAnalytics: true,
},
});

2. Monitor Cost Alerts

const COST_ALERT_THRESHOLD = 0.1; // $0.10 per request

async function monitoredGenerate(options: GenerateOptions) {
const result = await neurolink.generate({
...options,
enableAnalytics: true,
});

if (result.analytics?.cost && result.analytics.cost > COST_ALERT_THRESHOLD) {
console.warn(
`High cost alert: $${result.analytics.cost.toFixed(4)} for request`,
);
}

return result;
}

3. Track Token Efficiency

function calculateEfficiency(analytics: AnalyticsData): number {
// Ratio of output tokens to total tokens
const { input, output, total } = analytics.tokenUsage;
return total > 0 ? output / total : 0;
}

4. Implement Budget Controls

class BudgetController {
private dailyBudget: number;
private spent = 0;

constructor(dailyBudget: number) {
this.dailyBudget = dailyBudget;
}

async generate(options: GenerateOptions): Promise<GenerateResult> {
if (this.spent >= this.dailyBudget) {
throw new Error("Daily budget exceeded");
}

const result = await neurolink.generate({
...options,
enableAnalytics: true,
});

this.spent += result.analytics?.cost || 0;
return result;
}
}