File size: 6,877 Bytes
a50c30f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee8dccf
a50c30f
 
 
 
 
 
ee8dccf
 
a50c30f
ee8dccf
 
 
a50c30f
ee8dccf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50c30f
 
 
 
ee8dccf
 
 
a50c30f
ee8dccf
 
a50c30f
 
ee8dccf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50c30f
 
ee8dccf
 
 
 
 
 
 
 
 
 
 
 
 
 
a50c30f
ee8dccf
 
a50c30f
 
 
ee8dccf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a50c30f
 
 
 
ee8dccf
a50c30f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

// Document content storage
let documentContent = '';

// Process uploaded document
async function processDocument(file) {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = (e) => {
            documentContent = e.target.result;
            resolve({ success: true });
        };
        reader.readAsText(file);
    });
}
// Enhanced document search with better matching and context
function searchDocument(question) {
    if (!documentContent) return "Please upload a document first.";

    const questionLower = question.toLowerCase();
    const docLower = documentContent.toLowerCase();
    
    // Split into meaningful units (paragraphs or sections)
    const sections = documentContent.split(/\n\s*\n+/);
    
    // First try exact matches or very close matches
    for (const section of sections) {
        const sectionLower = section.toLowerCase();
        
        // Check for direct question matches
        if (sectionLower.includes(questionLower)) {
            return extractBestAnswer(section, questionLower);
        }
        
        // Check for "what is" questions
        if (questionLower.startsWith('what is') || questionLower.startsWith('what are')) {
            const targetTerm = questionLower.split(' ').slice(2).join(' ');
            if (sectionLower.includes(targetTerm)) {
                return extractDefinition(section, targetTerm);
            }
        }
        
        // Check for "how to" questions
        if (questionLower.startsWith('how to') || questionLower.startsWith('how do')) {
            const action = questionLower.split(' ').slice(2).join(' ');
            if (sectionLower.includes(action)) {
                return extractInstructions(section, action);
            }
        }
    }

    // Try semantic matching with weighted keywords
    const keywords = extractKeywords(questionLower);
    let bestMatch = { text: '', score: 0 };
    
    for (const section of sections) {
        const sectionLower = section.toLowerCase();
        let score = 0;
        
        for (const keyword of keywords) {
            if (sectionLower.includes(keyword)) {
                score += keyword.weight;
                
                // Bonus for proximity to other keywords
                const keywordPos = sectionLower.indexOf(keyword);
                for (const otherKeyword of keywords) {
                    if (otherKeyword !== keyword) {
                        const otherPos = sectionLower.indexOf(otherKeyword);
                        if (otherPos !== -1 && Math.abs(keywordPos - otherPos) < 100) {
                            score += 5;
                        }
                    }
                }
            }
        }
        
        if (score > bestMatch.score) {
            bestMatch = { text: section, score };
        }
    }
    
    if (bestMatch.score > 0) {
        return extractRelevantPart(bestMatch.text, keywords);
    }
    
    // As last resort, try to find any relevant information
    const relevant = findAnyRelevant(questionLower, sections);
    if (relevant) return relevant;
    
    return "I found some information that might be relevant, but couldn't find a direct answer to your question. Try asking differently or check these sections:\n" + 
           findPossibleSections(questionLower, sections);
}

// Helper functions
function extractKeywords(question) {
    const words = question.split(' ').filter(w => w.length > 3);
    const weights = {
        'what': 1, 'how': 2, 'why': 2, 'when': 2, 'where': 2,
        'is': 1, 'are': 1, 'does': 2, 'do': 1, 'can': 2,
        'explain': 3, 'describe': 3, 'define': 3, 'list': 2
    };
    
    return words.map(word => ({
        word: word,
        weight: weights[word] || 1
    }));
}

function extractBestAnswer(text, question) {
    // Split into sentences
    const sentences = text.split(/[.!?]+/).map(s => s.trim()).filter(s => s);
    
    // Find the most relevant sentence
    for (const sentence of sentences) {
        if (sentence.toLowerCase().includes(question)) {
            return sentence + '.';
        }
    }
    
    // If no exact match, return first few sentences
    return sentences.slice(0, 3).join(' ') + '...';
}

function extractDefinition(text, term) {
    const sentences = text.split(/[.!?]+/);
    for (let i = 0; i < sentences.length; i++) {
        if (sentences[i].toLowerCase().includes(term)) {
            // Try to get the definition sentence
            if (sentences[i].toLowerCase().includes('is') || sentences[i].toLowerCase().includes('are')) {
                return sentences[i].trim() + '.';
            }
            // Or combine with next sentence
            if (i < sentences.length - 1) {
                return (sentences[i] + ' ' + sentences[i+1]).trim() + '.';
            }
        }
    }
    return text.split('\n')[0]; // Fallback to first line
}

function findAnyRelevant(question, sections) {
    const keywords = extractKeywords(question).map(k => k.word);
    
    for (const section of sections) {
        const sectionLower = section.toLowerCase();
        let matches = 0;
        
        for (const keyword of keywords) {
            if (sectionLower.includes(keyword)) matches++;
        }
        
        if (matches >= keywords.length / 2) {
            return "This might help:\n" + section.split('\n').slice(0, 3).join('\n') + '...';
        }
    }
    
    return null;
}

function findPossibleSections(question, sections) {
    const keywords = extractKeywords(question).map(k => k.word);
    const relevantSections = [];
    
    for (const section of sections) {
        const sectionLower = section.toLowerCase();
        let matches = 0;
        
        for (const keyword of keywords) {
            if (sectionLower.includes(keyword)) matches++;
        }
        
        if (matches > 0) {
            const firstLine = section.split('\n')[0];
            if (firstLine && !relevantSections.includes(firstLine)) {
                relevantSections.push(firstLine);
            }
        }
    }
    
    return relevantSections.slice(0, 3).map(s => "• " + s).join('\n');
}
async function askQuestion(question) {
    if (!documentContent) {
        return { answer: "Please upload a document first." };
    }
    
    const answer = searchDocument(question);
    
    // Enhance simple answers with more context
    let enhancedAnswer = answer;
    if (answer.includes('is') && !answer.includes('.')) {
        // Try to find the next sentence for more context
        const sentences = documentContent.split(/[.!?]+/);
        const idx = sentences.findIndex(s => s.trim() + '.' === answer);
        if (idx !== -1 && idx < sentences.length - 1) {
            enhancedAnswer += ' ' + sentences[idx+1].trim() + '.';
        }
    }
    
    return { answer: enhancedAnswer };
}