| title | Analytics Reference |
|---|---|
| description | Comprehensive guide to NeuroLink analytics, metrics, and usage tracking |
NeuroLink provides comprehensive analytics capabilities for tracking token usage, costs, performance metrics, and quality evaluation across your configured AI provider interactions.
The analytics system in NeuroLink consists of several interconnected components:
| Component | Purpose |
|---|---|
| Token Usage Tracking | Monitor input/output tokens, cache tokens, and reasoning tokens |
| Cost Analytics | Estimate and track costs across providers and models |
| Performance Metrics | Measure response times, throughput, and memory usage |
| Quality Evaluation | Assess response relevance, accuracy, and completeness |
| Middleware Integration | Automatic analytics collection via middleware |
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);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;
};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}%`);
}
}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}`,
);
}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)}`);
}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;NeuroLink uses configurable pricing for each provider:
| Provider | Default 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 |
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.0008Track 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}`);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`);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>;
};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;
};
};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;
};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}`);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,
});
}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;
};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 can use different providers:
type EvaluationProvider =
| "openai"
| "anthropic"
| "vertex"
| "google-ai"
| "local";NeuroLink provides built-in analytics middleware:
import { createAnalyticsMiddleware } from "@juspay/neurolink";
const analyticsMiddleware = createAnalyticsMiddleware();
const neurolink = new NeuroLink();
const result = await neurolink.generate({
input: { text: "Explain quantum computing in simple terms" },
enableAnalytics: true,
middleware: {
middleware: [analyticsMiddleware],
enabledMiddleware: ["analytics"],
},
});The same two fields attach your own middleware (see Custom Analytics Collection below): registering it with middleware does not switch it on, so list its metadata.id in enabledMiddleware as well.
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,
};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;
}
},
};
}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();
}
});
}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);
}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);
}
}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;
}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
`;
}
}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);
}
}# 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# Detailed analytics output
neurolink generate "Explain AI" --enableAnalytics --verbose
# Shows full analytics breakdown including:
# - Token usage by type
# - Cost breakdown
# - Response time
# - Provider/model infoconst neurolink = new NeuroLink({
// Default analytics enabled for all requests
defaultOptions: {
enableAnalytics: true,
},
});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;
}function calculateEfficiency(analytics: AnalyticsData): number {
// Ratio of output tokens to total tokens
const { input, output, total } = analytics.tokenUsage;
return total > 0 ? output / total : 0;
}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;
}
}- Configuration Reference - Configure analytics settings
- Provider Comparison - Compare provider costs
- Troubleshooting - Debug analytics issues
- Error Codes - Analytics-related error codes