UNPKG

@osohq/langchain

Version:

Oso observability integration for LangChain agents

297 lines 12.7 kB
"use strict"; /** * LangChain callback handler for Oso observability integration. * * Example usage: * import { OsoObservabilityCallback } from '@osohq/langchain'; * * const callback = new OsoObservabilityCallback({ * authToken: "your-oso-auth-token", * agentId: "my-support-agent" * }); * * const agent = createAgent({ callbacks: [callback] }); * const result = await agent.invoke({ input: "Hello" }); * await callback.close(); */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OsoObservabilityCallback = void 0; const base_1 = require("@langchain/core/callbacks/base"); const cross_fetch_1 = __importDefault(require("cross-fetch")); const crypto_1 = require("crypto"); /** * LangChain callback handler that sends agent events to Oso observability. * * Automatically captures: * - LLM calls (model, prompts, responses, token usage) * - Tool executions (name, inputs, outputs, duration, errors) * - Agent reasoning (decisions, thoughts, intermediate steps) * - Chain executions (starts, ends, inputs, outputs) * - Errors at any stage */ class OsoObservabilityCallback extends base_1.BaseCallbackHandler { constructor(config = {}) { super(); this.name = "OsoObservabilityCallback"; // Accumulate data for summary event this.llmCalls = []; this.toolCalls = []; this.agentSteps = []; this.errors = []; this.endpoint = config.endpoint || process.env.OSO_ENDPOINT || "https://cloud.osohq.com/api/events"; this.authToken = config.authToken || process.env.OSO_AUTH_TOKEN; this.enabled = config.enabled !== undefined ? config.enabled : (process.env.OSO_OBSERVABILITY_ENABLED || "true").toLowerCase() === "true"; this.sessionId = config.sessionId || (0, crypto_1.randomUUID)(); this.metadata = config.metadata || {}; this.agentId = config.agentId || "default-agent"; this.executionId = (0, crypto_1.randomUUID)(); this.executionStartTime = Date.now(); this.toolStartTimes = new Map(); console.debug(`OsoObservabilityCallback initialized: endpoint=${this.endpoint}, ` + `agentId=${this.agentId}, sessionId=${this.sessionId}, enabled=${this.enabled}`); } // LLM callbacks handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata) { return __awaiter(this, void 0, void 0, function* () { var _a; const modelName = (llm === null || llm === void 0 ? void 0 : llm.name) || ((_a = llm === null || llm === void 0 ? void 0 : llm.id) === null || _a === void 0 ? void 0 : _a[llm.id.length - 1]) || "unknown"; yield this.sendEvent("llm.started", { model: modelName, prompts, num_prompts: prompts.length, }); }); } handleLLMEnd(output, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { try { const llmOutput = output.llmOutput || {}; const tokenUsage = llmOutput.tokenUsage || {}; const generations = []; if (output.generations) { for (const genList of output.generations) { for (const gen of genList) { generations.push({ text: gen.text || String(gen), metadata: gen.generationInfo || {}, }); } } } const callData = { model: llmOutput.modelName || "unknown", generations, token_usage: { prompt_tokens: tokenUsage.promptTokens || 0, completion_tokens: tokenUsage.completionTokens || 0, total_tokens: tokenUsage.totalTokens || 0, }, }; this.llmCalls.push(callData); yield this.sendEvent("llm.completed", callData); } catch (error) { console.error(`Error in handleLLMEnd: ${error}`); yield this.sendEvent("llm.error", { error: String(error) }); } }); } handleLLMError(error, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const errorData = { error_type: error.constructor.name, error_message: error.message, }; this.errors.push(Object.assign({ type: "llm_error" }, errorData)); yield this.sendEvent("llm.error", errorData); }); } // Tool callbacks handleToolStart(tool, input, runId, parentRunId, tags, metadata) { return __awaiter(this, void 0, void 0, function* () { const toolName = (tool === null || tool === void 0 ? void 0 : tool.name) || "unknown"; const toolDescription = (tool === null || tool === void 0 ? void 0 : tool.description) || ""; this.toolStartTimes.set(runId, Date.now()); yield this.sendEvent("tool.started", { tool_name: toolName, tool_description: toolDescription, input, run_id: runId, }); }); } handleToolEnd(output, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const startTime = this.toolStartTimes.get(runId); const durationMs = startTime ? Date.now() - startTime : null; const toolData = { output, duration_ms: durationMs, run_id: runId, }; this.toolCalls.push(toolData); yield this.sendEvent("tool.completed", toolData); this.toolStartTimes.delete(runId); }); } handleToolError(error, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const startTime = this.toolStartTimes.get(runId); const durationMs = startTime ? Date.now() - startTime : null; const errorData = { error_type: error.constructor.name, error_message: error.message, duration_ms: durationMs, run_id: runId, }; this.errors.push(Object.assign({ type: "tool_error" }, errorData)); yield this.sendEvent("tool.error", errorData); this.toolStartTimes.delete(runId); }); } // Agent callbacks handleAgentAction(action, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const stepData = { step_type: "action", tool: action.tool, tool_input: action.toolInput, reasoning: action.log, }; this.agentSteps.push(stepData); yield this.sendEvent("agent.action", stepData); }); } handleAgentEnd(action, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const finishData = { return_values: action.returnValues, final_reasoning: action.log || null, }; yield this.sendEvent("agent.finished", finishData); yield this.sendExecutionSummary(); }); } // Chain callbacks handleChainStart(chain, inputs, runId, parentRunId, tags, metadata) { return __awaiter(this, void 0, void 0, function* () { var _a; const chainName = (chain === null || chain === void 0 ? void 0 : chain.name) || ((_a = chain === null || chain === void 0 ? void 0 : chain.id) === null || _a === void 0 ? void 0 : _a[chain.id.length - 1]) || "unknown"; yield this.sendEvent("chain.started", { chain_name: chainName, inputs, }); }); } handleChainEnd(outputs, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { yield this.sendEvent("chain.completed", { outputs }); }); } handleChainError(error, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const errorData = { error_type: error.constructor.name, error_message: error.message, }; this.errors.push(Object.assign({ type: "chain_error" }, errorData)); yield this.sendEvent("chain.error", errorData); }); } // Internal methods sendEvent(eventType, data) { return __awaiter(this, void 0, void 0, function* () { if (!this.enabled) { return; } try { const payload = { event_type: eventType, execution_id: this.executionId, session_id: this.sessionId, timestamp: new Date().toISOString().replace("+00:00", "Z"), data, metadata: this.metadata, agent_id: this.agentId, }; console.debug(`Sending event: ${eventType}`); const headers = { "Content-Type": "application/json", }; if (this.authToken) { headers["Authorization"] = `Bearer ${this.authToken}`; } const response = yield (0, cross_fetch_1.default)(this.endpoint, { method: "POST", headers, body: JSON.stringify(payload), }); if (![200, 201, 202].includes(response.status)) { console.warn(`Oso backend returned ${response.status} for event ${eventType}`); } else { console.debug(`Event ${eventType} sent successfully`); } } catch (error) { if (error instanceof Error) { console.warn(`Error sending event ${eventType}: ${error.constructor.name}: ${error.message}`); } else { console.error(`Error sending event ${eventType}:`, error); } } }); } sendExecutionSummary() { return __awaiter(this, void 0, void 0, function* () { const durationMs = Date.now() - this.executionStartTime; const totalTokens = this.llmCalls.reduce((sum, call) => sum + (call.token_usage.total_tokens || 0), 0); const summary = { execution_id: this.executionId, session_id: this.sessionId, duration_ms: durationMs, llm_calls_count: this.llmCalls.length, tool_calls_count: this.toolCalls.length, agent_steps_count: this.agentSteps.length, errors_count: this.errors.length, total_tokens: totalTokens, llm_calls: this.llmCalls, tool_calls: this.toolCalls, agent_steps: this.agentSteps, errors: this.errors, }; yield this.sendEvent("execution.summary", summary); }); } /** * Clean up resources. Call this when you're done using the callback. */ close() { return __awaiter(this, void 0, void 0, function* () { console.debug("OsoObservabilityCallback closed"); // No persistent connections to close in this implementation }); } } exports.OsoObservabilityCallback = OsoObservabilityCallback; //# sourceMappingURL=callbacks.js.map