UNPKG

stock-nse-india

Version:

This package will help us to get equity/index details and historical data from National Stock Exchange of India.

347 lines (343 loc) 14.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContextSummarizer = void 0; class ContextSummarizer { constructor(openai, config = {}) { this.openai = openai; this.config = { maxTokens: 8000, reservedTokens: 2000, summarizationThreshold: 0.7, minMessagesToSummarize: 10, summaryCompressionRatio: 0.3, ...config }; } /** * Estimate token count for a message (rough approximation) */ estimateTokenCount(text) { // Rough estimation: 1 token ≈ 4 characters for English text // This is a simplified approach - in production, use tiktoken or similar if (!text || typeof text !== 'string') { return 0; } return Math.ceil(text.length / 4); } /** * Count tokens for a conversation */ countTokens(messages, systemPrompt) { const systemPromptTokens = this.estimateTokenCount(systemPrompt); const messageTokens = messages.map(msg => { if (msg && msg.content) { const tokens = this.estimateTokenCount(msg.content); // Ensure we return a valid number return isNaN(tokens) ? 0 : tokens; } return 0; }); // Filter out any NaN or invalid values before reducing const validTokens = messageTokens.filter(count => typeof count === 'number' && !isNaN(count)); const totalMessageTokens = validTokens.reduce((sum, count) => sum + count, 0); const totalTokens = systemPromptTokens + totalMessageTokens + this.config.reservedTokens; return { totalTokens, messageTokens, systemPromptTokens, estimatedResponseTokens: this.config.reservedTokens }; } /** * Check if context needs summarization */ needsSummarization(messages, systemPrompt) { const tokenCount = this.countTokens(messages, systemPrompt); // Calculate threshold based on maxTokens (not availableTokens) // because tokenCount.totalTokens already includes reservedTokens const threshold = this.config.maxTokens * this.config.summarizationThreshold; return (messages.length >= this.config.minMessagesToSummarize && tokenCount.totalTokens > threshold); } /** * Extract key information from messages for summarization */ extractKeyInformation(messages) { const stocks = new Set(); const indices = new Set(); const queries = []; const tools = new Set(); const preferences = {}; messages.forEach(msg => { var _a; if (msg.role === 'user' && msg.content) { queries.push(msg.content); // Extract stock symbols (simple regex) const stockMatches = msg.content.match(/\b[A-Z]{2,10}\b/g) || []; stockMatches.forEach((symbol) => { if (!['THE', 'AND', 'OR', 'FOR', 'WITH', 'FROM', 'TO', 'IN', 'ON', 'AT', 'BY'].includes(symbol)) { stocks.add(symbol); } }); // Extract indices const indexKeywords = ['nifty', 'banknifty', 'sensex', 'midcap', 'smallcap']; indexKeywords.forEach(keyword => { if (msg.content.toLowerCase().includes(keyword)) { indices.add(keyword.toUpperCase()); } }); } if (msg.tools_used) { msg.tools_used.forEach((tool) => tools.add(tool)); } if ((_a = msg.metadata) === null || _a === void 0 ? void 0 : _a.preferences) { Object.assign(preferences, msg.metadata.preferences); } }); return { stocks, indices, queries, tools, preferences }; } /** * Create a context summary using AI */ async createContextSummary(messages) { var _a, _b; const keyInfo = this.extractKeyInformation(messages); // Create a summary prompt const summaryPrompt = `Please create a concise summary of this conversation about Indian stock market data. Focus on: 1. Key topics discussed 2. Important stock symbols mentioned 3. Important indices mentioned 4. User preferences and analysis style 5. Tools used 6. Main queries and their outcomes Conversation to summarize: ${messages.map((msg, i) => `${i + 1}. [${msg.role}]: ${msg.content}`).join('\n')} Please provide: - A brief summary (2-3 sentences) - Key points (bullet list) - Important stocks mentioned - Important indices mentioned - User preferences detected - Tools used Format as JSON with these fields: summary, keyPoints, importantStocks, importantIndices, userPreferences, toolsUsed`; try { const response = await this.openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: 'You are a helpful assistant that creates concise summaries of conversations. ' + 'Always respond with valid JSON only, without any markdown formatting or code blocks.' }, { role: 'user', content: summaryPrompt } ], temperature: 0.3, max_tokens: 500 }); let summaryText = ((_b = (_a = response.choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content) || '{}'; // Clean up the response - remove markdown code blocks if present summaryText = summaryText.trim(); if (summaryText.startsWith('```json')) { summaryText = summaryText.replace(/^```json\s*/, '').replace(/\s*```$/, ''); } else if (summaryText.startsWith('```')) { summaryText = summaryText.replace(/^```\s*/, '').replace(/\s*```$/, ''); } summaryText = summaryText.trim(); let summaryData; try { summaryData = JSON.parse(summaryText); } catch (parseError) { console.error('Failed to parse OpenAI summary response:', summaryText.substring(0, 200)); throw parseError; } return { summary: summaryData.summary || 'Conversation summary', keyPoints: summaryData.keyPoints || [], importantStocks: summaryData.importantStocks || Array.from(keyInfo.stocks), importantIndices: summaryData.importantIndices || Array.from(keyInfo.indices), userPreferences: summaryData.userPreferences || keyInfo.preferences, timestamp: new Date().toISOString(), originalMessageCount: messages.length }; } catch (error) { // Fallback to simple extraction if AI summarization fails console.warn('AI summarization failed, using fallback method:', error instanceof Error ? error.message : String(error)); return { summary: `Conversation with ${messages.length} messages about Indian stock market data`, keyPoints: keyInfo.queries.slice(0, 5), importantStocks: Array.from(keyInfo.stocks), importantIndices: Array.from(keyInfo.indices), userPreferences: keyInfo.preferences, timestamp: new Date().toISOString(), originalMessageCount: messages.length }; } } /** * Optimize context by selecting most relevant messages */ async optimizeContext(messages, systemPrompt, maxTokens) { const tokenCount = this.countTokens(messages, systemPrompt); if (tokenCount.totalTokens <= maxTokens) { return { selectedMessages: messages, wasSummarized: false }; } // If we need to reduce context, prioritize recent messages and important ones const availableTokens = maxTokens - this.config.reservedTokens; const systemPromptTokens = tokenCount.systemPromptTokens; const targetMessageTokens = availableTokens - systemPromptTokens; // Sort messages by importance (recent + with tools + user queries) const scoredMessages = messages.map((msg, index) => { let score = 0; // Recent messages get higher score score += (messages.length - index) * 10; // Messages with tools get higher score if (msg.tools_used && msg.tools_used.length > 0) { score += 50; } // User queries get higher score if (msg.role === 'user') { score += 30; } // Assistant responses get medium score if (msg.role === 'assistant') { score += 20; } return { message: msg, score, index }; }); // Sort by score (highest first) scoredMessages.sort((a, b) => b.score - a.score); // Select messages that fit within token limit const selectedMessages = []; let currentTokens = 0; for (const { message } of scoredMessages) { const messageTokens = this.estimateTokenCount(message.content || ''); if (currentTokens + messageTokens <= targetMessageTokens) { selectedMessages.unshift(message); // Add to beginning to maintain order currentTokens += messageTokens; } else { break; } } // If we still have too many messages, create a summary if (selectedMessages.length < messages.length * 0.5) { return await this.createSummarizedContext(messages, systemPrompt, maxTokens); } return { selectedMessages, wasSummarized: false }; } /** * Create summarized context when too much history */ async createSummarizedContext(messages, systemPrompt, maxTokens) { // Calculate how many recent messages to keep // Target: keep enough to be around 40% of max tokens after summarization // This gives room for more conversation before next summarization const targetTokensAfterSummarization = maxTokens * 0.4; // Start with keeping more messages and work backwards let recentMessageCount = Math.min(10, messages.length - 1); // Keep at least 1 for summary let recentMessages = messages.slice(-recentMessageCount); // Adjust to fit target token count while (recentMessageCount > 2) { const testTokens = this.countTokens(recentMessages, systemPrompt); if (testTokens.totalTokens <= targetTokensAfterSummarization) { break; } recentMessageCount -= 2; // Remove one conversation pair at a time recentMessages = messages.slice(-recentMessageCount); } // Ensure we keep at least 2 messages (1 pair) if (recentMessageCount < 2) { recentMessageCount = Math.min(2, messages.length); recentMessages = messages.slice(-recentMessageCount); } const olderMessages = messages.slice(0, -recentMessageCount); if (olderMessages.length === 0) { return { selectedMessages: recentMessages, wasSummarized: false }; } // Create summary of older messages const summary = await this.createContextSummary(olderMessages); // Create a summary message const summaryMessage = { role: 'system', content: `[CONTEXT SUMMARY] ${summary.summary}\n\nKey Points: ${summary.keyPoints.join(', ')}\n` + `Important Stocks: ${summary.importantStocks.join(', ')}\n` + `Important Indices: ${summary.importantIndices.join(', ')}\n` + `User Preferences: ${JSON.stringify(summary.userPreferences)}`, timestamp: summary.timestamp, metadata: { isSummary: true, originalMessageCount: summary.originalMessageCount, summaryData: summary } }; return { selectedMessages: [summaryMessage, ...recentMessages], summary, wasSummarized: true }; } /** * Get optimal context for a conversation */ async getOptimalContext(messages, systemPrompt, maxTokens) { const targetTokens = maxTokens || this.config.maxTokens; const tokenCount = this.countTokens(messages, systemPrompt); // Check if we should trigger summarization based on threshold const thresholdTokens = targetTokens * this.config.summarizationThreshold; // If we're within limits and below threshold, return as is if (tokenCount.totalTokens <= thresholdTokens) { return { messages, wasSummarized: false, tokenCount }; } // Check minimum message requirement if (messages.length < this.config.minMessagesToSummarize) { return { messages, wasSummarized: false, tokenCount }; } // We've exceeded the threshold, so we should summarize // Force summarization by calling createSummarizedContext directly const summarized = await this.createSummarizedContext(messages, systemPrompt, targetTokens); return { messages: summarized.selectedMessages, summary: summarized.summary, wasSummarized: summarized.wasSummarized, tokenCount: this.countTokens(summarized.selectedMessages, systemPrompt) }; } /** * Update configuration */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } /** * Get current configuration */ getConfig() { return { ...this.config }; } } exports.ContextSummarizer = ContextSummarizer; //# sourceMappingURL=context-summarizer.js.map