@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
139 lines • 4.92 kB
JavaScript
/**
* Arize Exporter
* Exports spans to Arize ML monitoring platform
*/
import { BaseExporter } from "./baseExporter.js";
/**
* Arize exporter for ML monitoring and prediction logs
* Supports feature tracking and model performance monitoring
*/
export class ArizeExporter extends BaseExporter {
spaceKey;
apiKey;
modelId;
modelVersion;
endpoint = "https://api.arize.com/v1";
constructor(config) {
super("arize", config);
this.spaceKey = config.spaceKey;
this.apiKey = config.apiKey;
this.modelId = config.modelId ?? "neurolink-ai";
this.modelVersion = config.modelVersion ?? "1.0.0";
}
async initialize() {
if (this.initialized) {
return;
}
this.initialized = true;
this.startFlushInterval(this.config.flushIntervalMs ?? 10000);
}
async exportSpan(span) {
const startTime = Date.now();
try {
const prediction = this.convertToArizePrediction(span);
const response = await fetch(`${this.endpoint}/log`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
"space-key": this.spaceKey,
},
body: JSON.stringify(prediction),
});
if (!response.ok) {
throw new Error(`Export failed: ${response.statusText}`);
}
return this.createSuccessResult(1, Date.now() - startTime);
}
catch (error) {
return this.createFailureResult([span.spanId], error instanceof Error ? error.message : String(error), Date.now() - startTime);
}
}
async exportBatch(spans) {
// Arize's /v1/log endpoint does not support batch payloads, so we send
// individual requests in parallel. This is intentional — not a missed
// optimization.
const results = await Promise.all(spans.map((s) => this.exportSpan(s)));
const successful = results.filter((r) => r.success).length;
const failed = spans.length - successful;
return {
success: failed === 0,
exportedCount: successful,
failedCount: failed,
errors: results.flatMap((r) => r.errors ?? []),
durationMs: results.reduce((sum, r) => sum + r.durationMs, 0),
};
}
async flush() {
if (!this.initialized || this.buffer.length === 0) {
return;
}
const spans = [...this.buffer];
this.buffer = [];
await this.exportBatch(spans);
}
async shutdown() {
await this.flush();
this.stopFlushInterval();
this.initialized = false;
}
async healthCheck() {
try {
await this.withRetry(() => this.ping(), "health check");
return this.createHealthStatus(true);
}
catch {
return this.createHealthStatus(false, ["Health check failed"]);
}
}
/**
* Verify connectivity to Arize API
*/
async ping() {
const response = await fetch(`${this.endpoint}/health`, {
method: "GET",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"space-key": this.spaceKey,
},
});
if (!response.ok && response.status !== 404) {
// 404 is acceptable as health endpoint may not exist
throw new Error(`Arize API unreachable: ${response.status}`);
}
}
/**
* Convert span to Arize prediction log format
*/
convertToArizePrediction(span) {
return {
space_key: this.spaceKey,
model_id: span.attributes["ai.model"] ?? this.modelId,
model_version: this.modelVersion,
prediction_id: span.spanId,
prediction_timestamp: new Date(span.startTime).getTime(),
features: {
provider: span.attributes["ai.provider"],
temperature: span.attributes["ai.temperature"],
max_tokens: span.attributes["ai.max_tokens"],
user_id: span.attributes["user.id"],
session_id: span.attributes["session.id"],
},
prediction: {
input: span.attributes["input"],
output: span.attributes["output"],
},
tags: {
span_type: span.type,
environment: this.config.environment,
},
latency_ms: span.durationMs,
token_count: {
prompt: span.attributes["ai.tokens.input"],
completion: span.attributes["ai.tokens.output"],
total: span.attributes["ai.tokens.total"],
},
};
}
}
//# sourceMappingURL=arizeExporter.js.map