UNPKG

@osohq/langchain

Version:

Oso integration for LangChain agents

283 lines • 13.5 kB
"use strict"; /** * LangChain callback handler for Oso integration. * * Example usage: * import { OsoCallback } from '@osohq/langchain'; * * const callback = new OsoCallback({ * apiKey: "your-oso-api-key", * agentId: "my-support-agent", * users: [{ type: "User", id: "user-123" }] * }); * * const agent = createAgent({ llm, tools, prompt }); * const result = await agent.invoke({ input: "Hello" }, { callbacks: [callback] }); */ 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.OsoCallback = 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. */ class OsoCallback extends base_1.BaseCallbackHandler { constructor(config = {}) { super(); this.name = "OsoCallback"; this.logger = config.logger || (() => { }); this.url = new URL("api/agents/event", config.url || process.env.OSO_URL || "https://cloud.osohq.com"); this.apiKey = config.apiKey || process.env.OSO_AUTH; if (!this.apiKey) { this.logger("warn", "No API key provided for OsoCallback -- no events will be sent."); } const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; if (config.sessionId && uuidRegex.test(config.sessionId)) { this.sessionId = config.sessionId; } else { if (config.sessionId) { this.logger("warn", `Invalid UUID provided for sessionId: ${config.sessionId}. Generating a new one.`); } this.sessionId = (0, crypto_1.randomUUID)(); } this.metadata = config.metadata || {}; this.agentId = config.agentId || "default-agent"; this.users = config.users || []; this.toolNames = new Map(); this.logger("debug", `OsoCallback initialized: url=${this.url}, ` + `agentId=${this.agentId}, sessionId=${this.sessionId}`); } getEventBase() { return Object.assign({ agent_id: this.agentId, session_id: this.sessionId, users: this.users, timestamp: new Date().toISOString() }, this.metadata); } // Tool callbacks handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) { return __awaiter(this, void 0, void 0, function* () { const toolName = (tool === null || tool === void 0 ? void 0 : tool.name) || runName || "unknown"; this.toolNames.set(runId, toolName); let toolParameters; try { toolParameters = JSON.parse(input); } catch (_a) { toolParameters = { input }; } yield this.sendEvent("tool_call_request", Object.assign(Object.assign({}, this.getEventBase()), { tool_name: toolName, tool_parameters: toolParameters })); }); } handleToolEnd(output, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const toolName = this.toolNames.get(runId) || "unknown"; let toolResult; try { toolResult = JSON.parse(output); } catch (_a) { toolResult = output; } yield this.sendEvent("tool_call_response", Object.assign(Object.assign({}, this.getEventBase()), { tool_name: toolName, tool_result: toolResult })); this.toolNames.delete(runId); }); } handleToolError(error, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { const toolName = this.toolNames.get(runId) || "unknown"; yield this.sendEvent("tool_call_error", Object.assign(Object.assign({}, this.getEventBase()), { tool_name: toolName, error: error.message })); this.toolNames.delete(runId); }); } // Chain callbacks handleChainStart(chain, inputs, runId, parentRunId, tags, metadata) { return __awaiter(this, void 0, void 0, function* () { if (!parentRunId) { this.rootRunId = runId; let message = ""; if (typeof inputs === "string") { message = inputs; } else if (inputs.input) { message = typeof inputs.input === "string" ? inputs.input : JSON.stringify(inputs.input); } else if (inputs.question) { message = typeof inputs.question === "string" ? inputs.question : JSON.stringify(inputs.question); } else if (inputs.messages && Array.isArray(inputs.messages)) { const lastMessage = inputs.messages[inputs.messages.length - 1]; message = (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) || JSON.stringify(lastMessage); } else { message = JSON.stringify(inputs); } this.logger("debug", `handleChainStart inputs: ${JSON.stringify(inputs)}`); try { const event = JSON.parse(message); if (event && typeof event === "object" && event.userInput && typeof event.userInput === "object") { const userInput = event.userInput; if (!("text" in userInput)) { return; } message = typeof userInput.text === "string" ? userInput.text : JSON.stringify(userInput.text); } } catch (_a) { } yield this.sendEvent("message_from_user", Object.assign(Object.assign({}, this.getEventBase()), { message })); } }); } handleChainEnd(outputs, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { if (runId === this.rootRunId) { let message = ""; if (typeof outputs === "string") { message = outputs; } else if (outputs.output) { message = typeof outputs.output === "string" ? outputs.output : JSON.stringify(outputs.output); } else if (outputs.text) { message = typeof outputs.text === "string" ? outputs.text : JSON.stringify(outputs.text); } else if (outputs.response) { message = typeof outputs.response === "string" ? outputs.response : JSON.stringify(outputs.response); } else if (outputs.messages && Array.isArray(outputs.messages)) { const lastMessage = outputs.messages[outputs.messages.length - 1]; message = (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) || JSON.stringify(lastMessage); } else { message = JSON.stringify(outputs); } this.logger("debug", `handleChainEnd outputs: ${JSON.stringify(outputs)}`); try { const event = JSON.parse(message); if (event && typeof event === "object" && event.userInput && typeof event.userInput === "object") { if ("messageHistory" in event && Array.isArray(event.messageHistory) && "userInput" in event && event.userInput !== null && typeof event.userInput === "object" && "toolCallId" in event.userInput && event.userInput.toolCallId && event.userInput.toolCallId !== null) { const toolCallId = event.userInput.toolCallId; const toolCallMessage = event.messageHistory.find((message) => "toolCalls" in message && Array.isArray(message.toolCalls) && message.toolCalls.find((toolCall) => "id" in toolCall && toolCall.id === toolCallId)); if (toolCallMessage) { const toolCall = toolCallMessage.toolCalls.find((toolCall) => "id" in toolCall && toolCall.id === toolCallId); if (toolCall && "name" in toolCall && "input" in toolCall) { yield this.sendEvent("tool_call_request", Object.assign(Object.assign({}, this.getEventBase()), { tool_name: toolCall.name, tool_parameters: toolCall.input })); if ("content" in event.userInput) { yield this.sendEvent("tool_call_response", Object.assign(Object.assign({}, this.getEventBase()), { tool_name: toolCall.name, tool_result: typeof event.userInput.content === "string" ? JSON.parse(event.userInput.content) : event.userInput.content })); } } } } if ("finalResult" in event) { if (event.finalResult === null) { return; } message = typeof event.finalResult === "string" ? event.finalResult : JSON.stringify(event.finalResult); } } } catch (_a) { } yield this.sendEvent("message_to_user", Object.assign(Object.assign({}, this.getEventBase()), { message })); this.rootRunId = undefined; } }); } handleChainError(error, runId, parentRunId) { return __awaiter(this, void 0, void 0, function* () { if (runId === this.rootRunId) { this.rootRunId = undefined; } }); } // Internal methods sendEvent(path, payload) { return __awaiter(this, void 0, void 0, function* () { if (!this.apiKey) { this.logger("warn", "Not sending event because no API key is set", { path, payload, }); return; } const url = `${this.url}/${path}`; try { this.logger("debug", `Sending event to ${path}`); const headers = { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }; const response = yield (0, cross_fetch_1.default)(url, { method: "POST", headers, body: JSON.stringify(payload), }); if (![200, 201, 202].includes(response.status)) { this.logger("warn", `Oso backend returned ${response.status} for ${path}`); const text = yield response.text(); this.logger("error", `Response body: ${text}`); } else { this.logger("debug", `Event sent successfully to ${path}`); } } catch (error) { if (error instanceof Error) { this.logger("error", `Error sending event to ${path}: ${error.constructor.name}: ${error.message}`); } else { this.logger("error", `Error sending event to ${path}: ${error}`); } } }); } } exports.OsoCallback = OsoCallback; //# sourceMappingURL=callbacks.js.map