@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
173 lines • 5.72 kB
JavaScript
/**
* @file Langfuse Adapter
* Integration with Langfuse for LLM observability
*/
import { logger } from "../../utils/logger.js";
import { observabilityHooks } from "./observabilityHooks.js";
/**
* Langfuse adapter for evaluation observability
*/
export class LangfuseAdapter {
_config;
_unsubscribers = [];
_traceIdMap = new Map();
constructor(config) {
this._config = {
scorePrefix: "eval",
includeMetadata: true,
tags: [],
sendPipelineScores: true,
sendScorerScores: true,
...config,
};
}
/**
* Start listening to evaluation events
*/
start() {
// Prevent duplicate subscriptions
if (this._unsubscribers.length > 0) {
return;
}
// Listen for scorer completions
if (this._config.sendScorerScores) {
const scorerUnsub = observabilityHooks.on("scorer:end", (event) => {
this._sendScorerScore(event.result, event.traceContext?.traceId);
});
this._unsubscribers.push(scorerUnsub);
}
// Listen for pipeline completions
if (this._config.sendPipelineScores) {
const pipelineUnsub = observabilityHooks.on("pipeline:end", (event) => {
this._sendPipelineScores(event.result, event.traceContext?.traceId);
});
this._unsubscribers.push(pipelineUnsub);
}
logger.debug("Langfuse adapter started");
}
/**
* Stop listening to events
*/
stop() {
for (const unsub of this._unsubscribers) {
unsub();
}
this._unsubscribers = [];
this._traceIdMap.clear();
logger.debug("Langfuse adapter stopped");
}
/**
* Send scorer score to Langfuse
*/
async _sendScorerScore(result, traceId) {
try {
const scoreName = `${this._config.scorePrefix}.${result.scorerId}`;
const normalizedValue = result.normalizedScore; // Already 0-1
await this._config.client.score({
name: scoreName,
value: normalizedValue,
traceId,
comment: result.reasoning,
metadata: this._config.includeMetadata
? {
passed: result.passed,
threshold: result.threshold,
computeTime: result.computeTime,
confidence: result.confidence,
...(result.metadata ?? {}),
}
: undefined,
});
logger.debug(`Sent score to Langfuse: ${scoreName}=${normalizedValue}`);
}
catch (error) {
logger.error("Failed to send score to Langfuse", {
scorerId: result.scorerId,
error: error instanceof Error ? error.message : String(error),
});
}
}
/**
* Send pipeline scores to Langfuse
*/
async _sendPipelineScores(result, externalTraceId) {
const traceId = externalTraceId ?? result.correlationId;
const pipelineName = result.pipelineConfig.name ?? "unnamed";
try {
// Send overall pipeline score
await this._config.client.score({
name: `${this._config.scorePrefix}.pipeline.${pipelineName}.overall`,
value: result.overallScore / 10, // Normalize to 0-1
traceId,
comment: `Pipeline evaluation: ${result.passed ? "PASSED" : "FAILED"}`,
metadata: this._config.includeMetadata
? {
passed: result.passed,
aggregationMethod: result.aggregationMethod,
scorerCount: result.scores.length,
totalComputeTime: result.totalComputeTime,
errorCount: result.errors.length,
}
: undefined,
});
logger.debug(`Sent pipeline score to Langfuse: ${pipelineName}=${result.overallScore / 10}`);
}
catch (error) {
logger.error("Failed to send pipeline score to Langfuse", {
pipelineName,
error: error instanceof Error ? error.message : String(error),
});
}
}
/**
* Manually send a score to Langfuse
*/
async sendScore(name, value, options) {
const scoreName = `${this._config.scorePrefix}.${name}`;
await this._config.client.score({
name: scoreName,
value,
traceId: options?.traceId,
comment: options?.comment,
metadata: options?.metadata,
});
}
/**
* Shutdown the adapter and flush any pending data
*/
async shutdown() {
this.stop();
if (this._config.client.shutdown) {
await this._config.client.shutdown();
}
}
}
/**
* Create a Langfuse adapter
*/
export function createLangfuseAdapter(config) {
return new LangfuseAdapter(config);
}
/**
* Create and start a Langfuse adapter
*/
export function startLangfuseAdapter(config) {
const adapter = new LangfuseAdapter(config);
adapter.start();
return adapter;
}
/**
* Helper: Create a mock Langfuse client for testing
*/
export function createMockLangfuseClient() {
const scores = [];
return {
scores,
score: async (params) => {
scores.push(params);
return { id: `score-${scores.length}` };
},
shutdown: async () => { },
};
}
//# sourceMappingURL=langfuseAdapter.js.map