@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
366 lines • 15.3 kB
JavaScript
/**
* Memory Routes
* Endpoints for conversation memory management
*/
import { withSpan } from "../../telemetry/withSpan.js";
import { tracers } from "../../telemetry/tracers.js";
import { createErrorResponse, IdParamSchema, SessionIdParamSchema, validateParams, } from "../utils/validation.js";
/**
* Wrap a route handler with an OTel span for HTTP observability.
*/
function tracedMemoryHandler(name, route, fn) {
return (ctx) => withSpan({
name,
tracer: tracers.http,
attributes: {
"http.route": route,
"http.request.id": ctx.requestId,
},
}, () => fn(ctx));
}
/**
* Handler: Get messages for a session
*/
async function handleGetSessionMessages(ctx) {
// Validate path params
const paramValidation = validateParams(IdParamSchema, ctx.params, ctx.requestId);
if (!paramValidation.success) {
return paramValidation.error;
}
const { id: sessionId } = paramValidation.data;
// Parse query params for pagination
const limitParam = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50;
const offsetParam = ctx.query.offset ? parseInt(ctx.query.offset, 10) : 0;
// Validate pagination params
const limit = isNaN(limitParam) || limitParam < 1 ? 50 : Math.min(limitParam, 100);
const offset = isNaN(offsetParam) || offsetParam < 0 ? 0 : offsetParam;
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
let messages = [];
// Handle Redis memory manager (has getUserSessionHistory)
if ("getUserSessionHistory" in memory) {
// For Redis, we need a userId - check query params or use a default
const userId = ctx.query.userId || "default";
const sessionMessages = await memory.getUserSessionHistory(userId, sessionId);
if (sessionMessages === null) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found`, undefined, ctx.requestId);
}
messages = sessionMessages;
}
// Handle in-memory manager (has getSession)
else if ("getSession" in memory) {
const session = memory.getSession(sessionId);
if (!session) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found`, undefined, ctx.requestId);
}
messages = session.messages;
}
// Fallback: try buildContextMessages if available
else if ("buildContextMessages" in memory) {
messages = await memory.buildContextMessages(sessionId);
// If no messages, session might not exist
if (messages.length === 0) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found or has no messages`, undefined, ctx.requestId);
}
}
const total = messages.length;
const paginatedMessages = messages.slice(offset, offset + limit);
return {
sessionId,
messages: paginatedMessages,
total,
limit,
offset,
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to get session messages", undefined, ctx.requestId);
}
}
/**
* Handler: Get session by ID
*/
async function handleGetSession(ctx) {
// Validate path params
const paramValidation = validateParams(IdParamSchema, ctx.params, ctx.requestId);
if (!paramValidation.success) {
return paramValidation.error;
}
const { id: sessionId } = paramValidation.data;
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
// Handle Redis memory manager (has getUserSessionObject or getUserSessionMetadata)
if ("getUserSessionObject" in memory) {
// For Redis, we need a userId - check query params or use a default
const userId = ctx.query.userId || "default";
const sessionObject = await memory.getUserSessionObject(userId, sessionId);
if (sessionObject === null) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found`, undefined, ctx.requestId);
}
return {
session: sessionObject,
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
// Handle in-memory manager (has getSession)
else if ("getSession" in memory) {
const session = memory.getSession(sessionId);
if (!session) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found`, undefined, ctx.requestId);
}
return {
session,
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
return createErrorResponse("MEMORY_ERROR", "Session retrieval not supported for this memory type", undefined, ctx.requestId);
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to get session", undefined, ctx.requestId);
}
}
/**
* Handler: List all conversation sessions
*/
async function handleListSessions(ctx) {
// Parse query params
const userId = ctx.query.userId;
const limitParam = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50;
const offsetParam = ctx.query.offset ? parseInt(ctx.query.offset, 10) : 0;
// Validate pagination params
const limit = isNaN(limitParam) || limitParam < 1 ? 50 : Math.min(limitParam, 100);
const offset = isNaN(offsetParam) || offsetParam < 0 ? 0 : offsetParam;
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
let sessions = [];
// Handle Redis memory manager (has getUserAllSessionsHistory or getUserSessions)
if ("getUserAllSessionsHistory" in memory && userId) {
sessions = await memory.getUserAllSessionsHistory(userId);
}
else if ("getUserSessions" in memory && userId) {
// Get session IDs and then fetch metadata for each
const sessionIds = await memory.getUserSessions(userId);
// If we can get metadata, do so
if ("getUserSessionMetadata" in memory) {
const metadataPromises = sessionIds.map((id) => memory.getUserSessionMetadata(userId, id));
const metadataResults = await Promise.all(metadataPromises);
sessions = metadataResults.filter((m) => m !== null);
}
else {
// Just return session IDs as basic objects
sessions = sessionIds.map((id) => ({ id }));
}
}
// Handle in-memory manager - iterate over internal sessions Map
else if ("getStats" in memory) {
// For in-memory, we can use getSession to check if sessions exist
// but we need to know the session IDs first. The in-memory manager
// doesn't expose a listSessions method, so we return stats info instead.
const stats = await memory.getStats?.();
return {
sessions: [],
total: stats?.totalSessions || 0,
limit,
offset,
message: "Session listing not fully supported for in-memory storage. Use /stats endpoint for session counts.",
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
const total = sessions.length;
const paginatedSessions = sessions.slice(offset, offset + limit);
return {
sessions: paginatedSessions,
total,
limit,
offset,
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to list sessions", undefined, ctx.requestId);
}
}
/**
* Handler: Get memory statistics
*/
async function handleGetStats(ctx) {
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
// Get memory statistics if available
const stats = await memory.getStats?.();
return {
available: true,
type: memory.constructor.name,
stats: stats || {
message: "Statistics not available for this memory type",
},
timestamp: new Date().toISOString(),
};
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to get statistics", undefined, ctx.requestId);
}
}
/**
* Handler: Clear a conversation session
*/
async function handleClearSession(ctx) {
// Validate params
const paramValidation = validateParams(SessionIdParamSchema, ctx.params, ctx.requestId);
if (!paramValidation.success) {
return paramValidation.error;
}
const { sessionId } = paramValidation.data;
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
// Use clearSession for ConversationMemoryManager
const cleared = await memory.clearSession?.(sessionId);
if (cleared === false) {
return createErrorResponse("SESSION_NOT_FOUND", `Session '${sessionId}' not found`, undefined, ctx.requestId);
}
return {
success: true,
sessionId,
message: "Session cleared successfully",
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to clear session", undefined, ctx.requestId);
}
}
/**
* Handler: Clear all conversation sessions
*/
async function handleClearAllSessions(ctx) {
const memory = ctx.neurolink.conversationMemory;
if (!memory) {
return createErrorResponse("MEMORY_UNAVAILABLE", "Conversation memory not available", undefined, ctx.requestId);
}
try {
// Use clearAllSessions for ConversationMemoryManager
await memory.clearAllSessions?.();
return {
success: true,
message: "All sessions cleared successfully",
metadata: {
timestamp: new Date().toISOString(),
requestId: ctx.requestId,
},
};
}
catch (error) {
return createErrorResponse("MEMORY_ERROR", error instanceof Error ? error.message : "Failed to clear sessions", undefined, ctx.requestId);
}
}
/**
* Handler: Check memory system health
*/
async function handleMemoryHealth(ctx) {
const memory = ctx.neurolink.conversationMemory;
return {
available: !!memory,
type: memory?.constructor.name || "none",
timestamp: new Date().toISOString(),
};
}
/**
* Create memory management routes
* Note: These routes provide a simplified interface to conversation memory.
* The actual implementation depends on the memory manager type (ConversationMemoryManager or RedisConversationMemoryManager).
*/
export function createMemoryRoutes(basePath = "/api") {
return {
prefix: `${basePath}/memory`,
routes: [
// Route order matters - most specific routes first
// GET /api/memory/sessions/:id/messages - Get messages for a session (most specific)
{
method: "GET",
path: `${basePath}/memory/sessions/:id/messages`,
handler: tracedMemoryHandler("neurolink.http.memory.getSessionMessages", `${basePath}/memory/sessions/:id/messages`, handleGetSessionMessages),
description: "Get messages for a session",
tags: ["memory"],
},
// GET /api/memory/sessions/:id - Get session by ID
{
method: "GET",
path: `${basePath}/memory/sessions/:id`,
handler: tracedMemoryHandler("neurolink.http.memory.getSession", `${basePath}/memory/sessions/:id`, handleGetSession),
description: "Get session by ID",
tags: ["memory"],
},
// GET /api/memory/sessions - List all conversation sessions
{
method: "GET",
path: `${basePath}/memory/sessions`,
handler: tracedMemoryHandler("neurolink.http.memory.listSessions", `${basePath}/memory/sessions`, handleListSessions),
description: "List all conversation sessions",
tags: ["memory"],
},
{
method: "GET",
path: `${basePath}/memory/stats`,
handler: tracedMemoryHandler("neurolink.http.memory.stats", `${basePath}/memory/stats`, handleGetStats),
description: "Get memory statistics",
tags: ["memory"],
},
{
method: "DELETE",
path: `${basePath}/memory/sessions/:sessionId`,
handler: tracedMemoryHandler("neurolink.http.memory.clearSession", `${basePath}/memory/sessions/:sessionId`, handleClearSession),
description: "Clear a conversation session",
tags: ["memory"],
},
{
method: "DELETE",
path: `${basePath}/memory/sessions`,
handler: tracedMemoryHandler("neurolink.http.memory.clearAllSessions", `${basePath}/memory/sessions`, handleClearAllSessions),
description: "Clear all conversation sessions",
tags: ["memory"],
},
{
method: "GET",
path: `${basePath}/memory/health`,
handler: tracedMemoryHandler("neurolink.http.memory.health", `${basePath}/memory/health`, handleMemoryHealth),
description: "Check memory system health",
tags: ["memory", "health"],
},
],
};
}
//# sourceMappingURL=memoryRoutes.js.map