stock-nse-india
Version:
This package will help us to get equity/index details and historical data from National Stock Exchange of India.
490 lines • 19.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryManager = void 0;
const fs = __importStar(require("fs"));
const context_summarizer_1 = require("./context-summarizer");
const openai_1 = __importDefault(require("openai"));
class MemoryManager {
constructor(config = {}) {
this.sessions = new Map();
this.config = {
maxConversationHistory: 50,
maxRecentQueries: 20,
sessionTimeoutMinutes: 30,
persistToFile: true,
memoryFilePath: './memory-data.json',
contextWindowConfig: {
maxTokens: 8000,
reservedTokens: 2000,
summarizationThreshold: 0.6,
minMessagesToSummarize: 6,
summaryCompressionRatio: 0.3
},
...config
};
this.memoryFilePath = this.config.memoryFilePath;
// Load any previously saved memory data synchronously so it's available immediately
this.loadMemoryFromFile();
}
getContextSummarizer() {
if (!this.contextSummarizer) {
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error('OpenAI API key is required for context summarization. Set OPENAI_API_KEY.');
}
this.contextSummarizer = new context_summarizer_1.ContextSummarizer(new openai_1.default({ apiKey }), this.config.contextWindowConfig);
}
return this.contextSummarizer;
}
/**
* Create or get existing user session
*/
getOrCreateSession(sessionId, userId) {
let session = this.sessions.get(sessionId);
if (!session) {
session = {
sessionId,
userId,
startTime: new Date().toISOString(),
lastActivity: new Date().toISOString(),
conversationHistory: [],
userPreferences: {
preferredStocks: [],
preferredIndices: [],
analysisStyle: 'detailed',
language: 'en',
timezone: 'Asia/Kolkata',
notificationSettings: {
priceAlerts: false,
marketUpdates: false
}
},
contextData: {
recentQueries: [],
frequentlyAccessedStocks: {},
frequentlyUsedTools: {},
marketContext: {},
userGoals: []
}
};
this.sessions.set(sessionId, session);
}
else {
// Update last activity
session.lastActivity = new Date().toISOString();
}
return session;
}
/**
* Add message to conversation history
*/
addMessage(sessionId, message) {
const session = this.getOrCreateSession(sessionId);
// Add message to history
session.conversationHistory.push(message);
// Trim history if it exceeds max length
if (session.conversationHistory.length > this.config.maxConversationHistory) {
session.conversationHistory = session.conversationHistory.slice(-this.config.maxConversationHistory);
}
// Update context data
if (message.role === 'user') {
this.updateRecentQueries(sessionId, message.content);
}
if (message.tools_used) {
this.updateToolUsage(sessionId, message.tools_used);
}
this.saveMemoryToFile();
}
/**
* Update recent queries
*/
updateRecentQueries(sessionId, query) {
const session = this.sessions.get(sessionId);
if (!session)
return;
// Add to recent queries
session.contextData.recentQueries.unshift(query);
// Trim if exceeds max length
if (session.contextData.recentQueries.length > this.config.maxRecentQueries) {
session.contextData.recentQueries = session.contextData.recentQueries.slice(0, this.config.maxRecentQueries);
}
}
/**
* Update tool usage statistics
*/
updateToolUsage(sessionId, tools) {
const session = this.sessions.get(sessionId);
if (!session)
return;
tools.forEach(tool => {
session.contextData.frequentlyUsedTools[tool] = (session.contextData.frequentlyUsedTools[tool] || 0) + 1;
});
}
/**
* Update stock access frequency
*/
updateStockAccess(sessionId, symbol) {
const session = this.sessions.get(sessionId);
if (!session)
return;
session.contextData.frequentlyAccessedStocks[symbol] =
(session.contextData.frequentlyAccessedStocks[symbol] || 0) + 1;
}
/**
* Update user preferences
*/
updatePreferences(sessionId, preferences) {
const session = this.sessions.get(sessionId);
if (!session)
return;
session.userPreferences = { ...session.userPreferences, ...preferences };
this.saveMemoryToFile();
}
/**
* Update market context
*/
updateMarketContext(sessionId, marketData) {
const session = this.sessions.get(sessionId);
if (!session)
return;
session.contextData.marketContext = {
...session.contextData.marketContext,
currentMarketStatus: marketData,
lastMarketUpdate: new Date().toISOString()
};
}
/**
* Get conversation context for AI with intelligent summarization
*/
async getConversationContext(sessionId, maxMessages, systemPrompt, persistSummarization = true) {
const session = this.sessions.get(sessionId);
if (!session)
return { messages: [], wasSummarized: false, tokenCount: { totalTokens: 0 } };
let messages = session.conversationHistory;
// Apply maxMessages limit if specified
if (maxMessages && messages.length > maxMessages) {
messages = messages.slice(-maxMessages);
}
// If no system prompt provided, use default
const prompt = systemPrompt || this.getContextualSystemPrompt(sessionId);
// Calculate tokens before optimization
const tokensBefore = this.getContextSummarizer().countTokens(messages, prompt);
// Get optimized context with summarization
const optimizedContext = await this.getContextSummarizer().getOptimalContext(messages, prompt);
// If summarization occurred and persistSummarization is true, update the session history
if (optimizedContext.wasSummarized && persistSummarization) {
// Calculate tokens saved
const tokensAfter = optimizedContext.tokenCount;
const tokensSaved = tokensBefore.totalTokens - tokensAfter.totalTokens;
// Create summarization record
const summarizationRecord = {
timestamp: new Date().toISOString(),
originalMessageCount: messages.length,
summarizedMessageCount: optimizedContext.messages.length,
originalMessages: messages,
summary: optimizedContext.summary,
tokensSaved: tokensSaved,
triggerReason: `Token threshold exceeded: ${tokensBefore.totalTokens} > ` +
`${this.config.contextWindowConfig.maxTokens *
(this.config.contextWindowConfig.summarizationThreshold || 0.7)}`
};
// Initialize summarization history if needed
if (!session.contextData.summarizationHistory) {
session.contextData.summarizationHistory = [];
}
// Add to history (keep last 10 summarizations)
session.contextData.summarizationHistory.push(summarizationRecord);
if (session.contextData.summarizationHistory.length > 10) {
session.contextData.summarizationHistory = session.contextData.summarizationHistory.slice(-10);
}
// Update last summarization
session.contextData.lastSummarization = summarizationRecord;
// Update conversation history with summarized version
session.conversationHistory = optimizedContext.messages;
this.saveMemoryToFile();
}
return {
messages: optimizedContext.messages,
summary: optimizedContext.summary,
wasSummarized: optimizedContext.wasSummarized,
tokenCount: optimizedContext.tokenCount
};
}
/**
* Get conversation context for AI (backward compatibility)
*/
getConversationContextSync(sessionId, maxMessages = 10) {
const session = this.sessions.get(sessionId);
if (!session)
return [];
return session.conversationHistory.slice(-maxMessages);
}
/**
* Get user context summary
*/
getUserContextSummary(sessionId) {
const session = this.sessions.get(sessionId);
if (!session)
return '';
const { userPreferences, contextData } = session;
let contextSummary = `User Preferences:\n`;
contextSummary += `- Analysis Style: ${userPreferences.analysisStyle}\n`;
contextSummary += `- Language: ${userPreferences.language}\n`;
contextSummary += `- Timezone: ${userPreferences.timezone}\n`;
if (userPreferences.preferredStocks.length > 0) {
contextSummary += `- Preferred Stocks: ${userPreferences.preferredStocks.join(', ')}\n`;
}
if (userPreferences.preferredIndices.length > 0) {
contextSummary += `- Preferred Indices: ${userPreferences.preferredIndices.join(', ')}\n`;
}
if (contextData.frequentlyAccessedStocks && Object.keys(contextData.frequentlyAccessedStocks).length > 0) {
const topStocks = Object.entries(contextData.frequentlyAccessedStocks)
.sort(([, a], [, b]) => b - a)
.slice(0, 5)
.map(([symbol]) => symbol);
contextSummary += `- Frequently Accessed Stocks: ${topStocks.join(', ')}\n`;
}
if (contextData.recentQueries.length > 0) {
contextSummary += `- Recent Queries: ${contextData.recentQueries.slice(0, 3).join('; ')}\n`;
}
if (contextData.userGoals.length > 0) {
contextSummary += `- User Goals: ${contextData.userGoals.join(', ')}\n`;
}
return contextSummary;
}
/**
* Get system prompt with context
*/
getContextualSystemPrompt(sessionId) {
const basePrompt = 'You are a helpful assistant that provides information about Indian stock market data from NSE India. ' +
'You have access to various tools to fetch real-time market data, stock information, ' +
'historical data, and more. ' +
'When a user asks a question, use the appropriate tools to gather the necessary data ' +
'and provide a comprehensive answer. ' +
'Always format your responses in a clear, professional manner with proper markdown ' +
'formatting when appropriate.';
const userContext = this.getUserContextSummary(sessionId);
if (userContext) {
return `${basePrompt}\n\nUser Context:\n${userContext}\n\n` +
`Use this context to provide personalized responses and remember user preferences.`;
}
return basePrompt;
}
/**
* Clean up expired sessions
*/
cleanupExpiredSessions() {
const now = new Date();
const timeoutMs = this.config.sessionTimeoutMinutes * 60 * 1000;
for (const [sessionId, session] of this.sessions.entries()) {
const lastActivity = new Date(session.lastActivity);
if (now.getTime() - lastActivity.getTime() > timeoutMs) {
this.sessions.delete(sessionId);
}
}
}
/**
* Save memory to file (synchronous to guarantee persistence before returning)
*/
saveMemoryToFile() {
if (!this.config.persistToFile)
return;
try {
const memoryData = {
sessions: Object.fromEntries(this.sessions),
config: this.config,
lastSaved: new Date().toISOString()
};
fs.writeFileSync(this.memoryFilePath, JSON.stringify(memoryData, null, 2));
}
catch (error) {
console.error('Failed to save memory to file:', error);
}
}
/**
* Load memory from file (synchronous so sessions are ready after construction)
*/
loadMemoryFromFile() {
if (!this.config.persistToFile)
return;
try {
const data = fs.readFileSync(this.memoryFilePath, 'utf-8');
const memoryData = JSON.parse(data);
if (memoryData.sessions) {
this.sessions = new Map(Object.entries(memoryData.sessions));
}
}
catch (error) {
// If file doesn't exist, start fresh silently; otherwise log a warning
if ((error === null || error === void 0 ? void 0 : error.code) !== 'ENOENT') {
console.warn('Failed to load memory from file, starting with empty memory:', error);
}
}
}
/**
* Get session statistics
*/
getSessionStats(sessionId) {
const session = this.sessions.get(sessionId);
if (!session)
return null;
return {
sessionId: session.sessionId,
userId: session.userId,
startTime: session.startTime,
lastActivity: session.lastActivity,
messageCount: session.conversationHistory.length,
recentQueriesCount: session.contextData.recentQueries.length,
frequentlyAccessedStocks: Object.keys(session.contextData.frequentlyAccessedStocks).length,
frequentlyUsedTools: Object.keys(session.contextData.frequentlyUsedTools).length
};
}
/**
* Export session data
*/
exportSessionData(sessionId) {
return this.sessions.get(sessionId);
}
/**
* Clear session data
*/
clearSession(sessionId) {
this.sessions.delete(sessionId);
this.saveMemoryToFile();
}
/**
* Check if context needs summarization for a session
*/
async needsContextSummarization(sessionId, systemPrompt) {
const session = this.sessions.get(sessionId);
if (!session)
return false;
const prompt = systemPrompt || this.getContextualSystemPrompt(sessionId);
return this.getContextSummarizer().needsSummarization(session.conversationHistory, prompt);
}
/**
* Get context statistics for a session
*/
async getContextStats(sessionId, systemPrompt) {
const session = this.sessions.get(sessionId);
if (!session) {
return {
messageCount: 0,
tokenCount: { totalTokens: 0 },
needsSummarization: false,
contextWindowUsage: 0
};
}
const prompt = systemPrompt || this.getContextualSystemPrompt(sessionId);
const tokenCount = this.getContextSummarizer().countTokens(session.conversationHistory, prompt);
const needsSummarization = await this.needsContextSummarization(sessionId, systemPrompt);
const contextWindowUsage = (tokenCount.totalTokens / this.getContextSummarizer().getConfig().maxTokens) * 100;
return {
messageCount: session.conversationHistory.length,
tokenCount,
needsSummarization,
contextWindowUsage
};
}
/**
* Force context summarization for a session
*/
async forceContextSummarization(sessionId, systemPrompt) {
const session = this.sessions.get(sessionId);
if (!session)
return null;
const prompt = systemPrompt || this.getContextualSystemPrompt(sessionId);
const summary = await this.getContextSummarizer().createContextSummary(session.conversationHistory);
// Store summary in session metadata
if (!session.contextData.marketContext) {
session.contextData.marketContext = {};
}
session.contextData.marketContext.lastSummary = summary;
this.saveMemoryToFile();
return summary;
}
/**
* Update context window configuration
*/
updateContextWindowConfig(config) {
this.config.contextWindowConfig = { ...this.config.contextWindowConfig, ...config };
this.getContextSummarizer().updateConfig(this.config.contextWindowConfig);
}
/**
* Get context window configuration
*/
getContextWindowConfig() {
return this.getContextSummarizer().getConfig();
}
/**
* Get last summarization for a session
*/
getLastSummarization(sessionId) {
const session = this.sessions.get(sessionId);
return (session === null || session === void 0 ? void 0 : session.contextData.lastSummarization) || null;
}
/**
* Get summarization history for a session
*/
getSummarizationHistory(sessionId, limit) {
const session = this.sessions.get(sessionId);
if (!session || !session.contextData.summarizationHistory) {
return [];
}
const history = session.contextData.summarizationHistory;
return limit ? history.slice(-limit) : history;
}
/**
* Get detailed summarization info (without original messages for lighter payload)
*/
getSummarizationSummary(sessionId) {
const session = this.sessions.get(sessionId);
if (!session)
return null;
const history = session.contextData.summarizationHistory || [];
const totalTokensSaved = history.reduce((sum, record) => sum + record.tokensSaved, 0);
const result = {
totalSummarizations: history.length,
totalTokensSaved
};
if (session.contextData.lastSummarization) {
const last = session.contextData.lastSummarization;
result.lastSummarization = {
timestamp: last.timestamp,
messagesBefore: last.originalMessageCount,
messagesAfter: last.summarizedMessageCount,
tokensSaved: last.tokensSaved,
summary: last.summary.summary
};
}
return result;
}
}
exports.MemoryManager = MemoryManager;
//# sourceMappingURL=memory-manager.js.map