Skip to content

Commit b630726

Browse files
authored
Merge pull request #1 from pinkpixel-dev/dev-research
feat: Enhance research tool with follow-up query handling and AI chat…
2 parents 4b8349a + ab95fd4 commit b630726

File tree

5 files changed

+392
-176
lines changed

5 files changed

+392
-176
lines changed

public/js/research-modules/document-generation.js

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
function formatMarkdown(markdown) {
1010
if (!markdown) return '';
11-
11+
1212
// Basic markdown formatting (can be enhanced)
1313
let html = markdown
1414
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
@@ -21,7 +21,7 @@ function formatMarkdown(markdown) {
2121
.replace(/\[(.*?)\]\((.*?)\)/gim, '<a href="$2">$1</a>')
2222
.replace(/\n/gim, '<br>')
2323
.replace(/^-(.*$)/gim, '<li>$1</li>');
24-
24+
2525
return html;
2626
}
2727

@@ -46,17 +46,41 @@ async function generateIntroduction(topic, format, context = '') {
4646
*/
4747
async function generateConclusion(topic, sources, format, context = '') {
4848
let conclusion = `## Conclusion\n\n${topic} is a complex and evolving field with many facets and applications. This document has presented a comprehensive overview based on research from multiple sources.\n\n`;
49-
49+
5050
// Add sources section
5151
conclusion += `## Sources\n\n`;
5252
if (sources && sources.length) {
5353
sources.forEach((source, index) => {
54-
conclusion += `${index + 1}. [${source}](${source})\n`;
54+
// Handle different source formats (string or object)
55+
let sourceUrl = '';
56+
let sourceTitle = '';
57+
58+
if (typeof source === 'string') {
59+
sourceUrl = source;
60+
sourceTitle = source;
61+
} else if (typeof source === 'object' && source !== null) {
62+
// Extract URL from object
63+
if (source.url) {
64+
sourceUrl = source.url;
65+
sourceTitle = source.title || source.url;
66+
} else if (source.href) {
67+
sourceUrl = source.href;
68+
sourceTitle = source.title || source.href;
69+
} else if (source.toString) {
70+
sourceUrl = source.toString();
71+
sourceTitle = sourceUrl;
72+
}
73+
}
74+
75+
// Only add valid URLs
76+
if (sourceUrl) {
77+
conclusion += `${index + 1}. [${sourceTitle}](${sourceUrl})\n`;
78+
}
5579
});
5680
} else {
5781
conclusion += `No sources were available for this document.`;
5882
}
59-
83+
6084
return conclusion;
6185
}
6286

@@ -73,7 +97,7 @@ async function processSourceContent(source, topic, sectionIndex, totalSections,
7397
if (!source || !source.content) {
7498
return generateFallbackContent(topic);
7599
}
76-
100+
77101
try {
78102
// Use the pollinations-text API endpoint for text generation
79103
const response = await fetch('/api/pollinations-text', {
@@ -89,20 +113,35 @@ async function processSourceContent(source, topic, sectionIndex, totalSections,
89113
max_tokens: 2000
90114
})
91115
});
92-
116+
93117
const result = await response.json();
94-
118+
95119
if (!result.success || !result.text || result.text.trim().length < 100) {
96120
console.log('Failed to generate content from source, using fallback', result);
97121
return generateFallbackContent(topic);
98122
}
99-
123+
100124
// Add source attribution
101125
let content = result.text;
102-
if (!content.includes(`Source: ${source.url}`)) {
103-
content += `\n\n*Source: [${source.url}](${source.url})*\n\n`;
126+
127+
// Get the source URL in a safe way
128+
let sourceUrl = '';
129+
if (typeof source.url === 'string') {
130+
sourceUrl = source.url;
131+
} else if (source.url && typeof source.url === 'object' && source.url.href) {
132+
sourceUrl = source.url.href;
133+
} else if (source.url && source.url.toString && source.url.toString() !== '[object Object]') {
134+
sourceUrl = source.url.toString();
104135
}
105-
136+
137+
// Get the source title
138+
let sourceTitle = source.title || sourceUrl;
139+
140+
// Add attribution if not already included and we have a valid URL
141+
if (sourceUrl && !content.includes(`Source: ${sourceUrl}`)) {
142+
content += `\n\n*Source: [${sourceTitle}](${sourceUrl})*\n\n`;
143+
}
144+
106145
return content;
107146
} catch (error) {
108147
console.error('Error generating document section:', error);
@@ -186,9 +225,9 @@ function displayDocument(documentContent, format = 'markdown') {
186225
const documentContainer = document.getElementById('document-container');
187226
const toggleDocumentBtn = document.getElementById('toggle-document-btn');
188227
const documentSidebar = document.getElementById('document-sidebar');
189-
228+
190229
if (!documentContainer) return;
191-
230+
192231
// Set document content
193232
if (format === 'markdown') {
194233
// For markdown, we need to handle some basic formatting
@@ -198,12 +237,12 @@ function displayDocument(documentContent, format = 'markdown') {
198237
// For HTML, we can set it directly
199238
documentContainer.innerHTML = documentContent;
200239
}
201-
240+
202241
// Show the document sidebar toggle button
203242
if (toggleDocumentBtn) {
204243
toggleDocumentBtn.style.display = 'block';
205244
}
206-
245+
207246
// Automatically open the document sidebar
208247
if (documentSidebar) {
209248
documentSidebar.classList.add('visible');
@@ -220,7 +259,7 @@ function downloadDocument(content, filename, format) {
220259
let downloadContent = content;
221260
let mimeType = 'text/plain';
222261
let extension = 'txt';
223-
262+
224263
if (format === 'html') {
225264
downloadContent = `<!DOCTYPE html>
226265
<html>
@@ -250,7 +289,7 @@ function downloadDocument(content, filename, format) {
250289
mimeType = 'text/markdown';
251290
extension = 'md';
252291
}
253-
292+
254293
const blob = new Blob([downloadContent], { type: mimeType });
255294
const url = URL.createObjectURL(blob);
256295
const a = document.createElement('a');
@@ -288,4 +327,4 @@ export {
288327
displayDocument,
289328
downloadDocument,
290329
copyToClipboard
291-
};
330+
};

public/js/research-modules/query-variation.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
*/
1010
async function generateQueryVariations(baseQuery, tier = 'quick') {
1111
console.log(`Generating query variations for tier: ${tier}`);
12-
12+
1313
// For quick tier, just return the original query
1414
if (tier === 'quick') {
1515
return [baseQuery];
1616
}
17-
17+
1818
// Default variations to use if API call fails
1919
const defaultVariations = {
2020
extended: [
@@ -43,20 +43,20 @@ async function generateQueryVariations(baseQuery, tier = 'quick') {
4343
max_tokens: 1000
4444
})
4545
});
46-
46+
4747
const result = await response.json();
48-
48+
4949
if (!result.success || !result.text) {
5050
console.log('Failed to generate query variations, using defaults');
5151
return [baseQuery, ...defaultVariations[tier]];
5252
}
53-
53+
5454
// Parse the response to get individual queries
5555
const variations = result.text
5656
.split('\n')
5757
.map(line => line.trim())
5858
.filter(line => line && !line.match(/^\d+\./)); // Remove any numbered items
59-
59+
6060
if (variations && variations.length > 0) {
6161
// Add the original query and deduplicate
6262
return [...new Set([baseQuery, ...variations])];
@@ -76,17 +76,17 @@ async function generateQueryVariations(baseQuery, tier = 'quick') {
7676
*/
7777
function determineIfClarificationNeeded(query) {
7878
if (!query) return true;
79-
79+
8080
// Check for very short queries
8181
if (query.length < 10) return true;
82-
82+
8383
// Check for queries without spaces (likely a single term)
8484
if (!query.includes(' ')) return true;
85-
85+
8686
// Check for very generic queries
8787
const genericTerms = ['how', 'what', 'who', 'why', 'when', 'where', 'is', 'are', 'can', 'do'];
8888
if (genericTerms.some(term => query.toLowerCase() === term)) return true;
89-
89+
9090
return false;
9191
}
9292

@@ -96,7 +96,16 @@ function determineIfClarificationNeeded(query) {
9696
* @returns {string} Initial response message
9797
*/
9898
function generateInitialResponse(query) {
99-
return `I'll research "${query}" for you. This might take a few minutes...`;
99+
const responses = [
100+
`I'm starting my research on "${query}" now. This might take a few minutes...`,
101+
`I'll gather comprehensive information about "${query}" for you. Please give me a moment...`,
102+
`Beginning research on "${query}". I'll compile the best sources and information for you...`,
103+
`I'm searching for high-quality information about "${query}". This will take a few minutes...`,
104+
`Starting to research "${query}" now. I'll create a detailed document for you shortly...`
105+
];
106+
107+
// Return a random response for variety
108+
return responses[Math.floor(Math.random() * responses.length)];
100109
}
101110

102111
/**
@@ -106,7 +115,7 @@ function generateInitialResponse(query) {
106115
*/
107116
function categorizeSources(sources) {
108117
if (!sources || !Array.isArray(sources)) return [];
109-
118+
110119
const sourceTypes = {
111120
documentation: {
112121
priority: 5,
@@ -162,4 +171,4 @@ export {
162171
determineIfClarificationNeeded,
163172
generateInitialResponse,
164173
categorizeSources
165-
};
174+
};

public/js/research-modules/research-core.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -283,17 +283,42 @@ function showSourcesList(sources) {
283283
if (!sourcesListContainer || !sourcesContainer) return;
284284

285285
// Create source list HTML
286-
const sourcesList = sources.map(source =>
287-
`<div class="source-item" style="margin-bottom: 10px;">
288-
<a href="${source.url}" target="_blank" rel="noopener noreferrer" style="color: var(--primary-color); text-decoration: none; font-weight: 500;">
289-
${source.title || source.url}
290-
</a>
291-
${source.type ? `<span class="source-type" style="margin-left: 8px; font-size: 12px; padding: 2px 6px; border-radius: 4px; background: var(--secondary-color-light); color: var(--text-color);">${source.type}</span>` : ''}
292-
</div>`
293-
).join('');
286+
const sourcesList = sources.map(source => {
287+
// Handle different source formats
288+
let sourceUrl = '';
289+
let sourceTitle = '';
290+
let sourceType = '';
291+
292+
if (typeof source === 'string') {
293+
sourceUrl = source;
294+
sourceTitle = source;
295+
} else if (typeof source === 'object' && source !== null) {
296+
// Extract URL from object
297+
if (source.url) {
298+
sourceUrl = source.url;
299+
sourceTitle = source.title || source.url;
300+
sourceType = source.type || '';
301+
} else if (source.href) {
302+
sourceUrl = source.href;
303+
sourceTitle = source.title || source.href;
304+
sourceType = source.type || '';
305+
}
306+
}
307+
308+
// Only create HTML for valid URLs
309+
if (sourceUrl) {
310+
return `<div class="source-item" style="margin-bottom: 10px;">
311+
<a href="${sourceUrl}" target="_blank" rel="noopener noreferrer" style="color: var(--primary-color); text-decoration: none; font-weight: 500;">
312+
${sourceTitle}
313+
</a>
314+
${sourceType ? `<span class="source-type" style="margin-left: 8px; font-size: 12px; padding: 2px 6px; border-radius: 4px; background: var(--secondary-color-light); color: var(--text-color);">${sourceType}</span>` : ''}
315+
</div>`;
316+
}
317+
return '';
318+
}).filter(item => item !== '').join('');
294319

295320
// Update the sources list
296-
sourcesListContainer.innerHTML = sourcesList;
321+
sourcesListContainer.innerHTML = sourcesList || 'No sources available';
297322

298323
// Show the sources container
299324
sourcesContainer.style.display = 'block';
@@ -414,7 +439,15 @@ async function performResearch(query, tier = 'quick', includeImages = false, sta
414439
// Generate conclusion with source references
415440
const conclusion = await generateConclusion(
416441
query,
417-
categorizedSources.map(source => source.url),
442+
categorizedSources.map(source => {
443+
// Ensure we're passing a valid URL string
444+
if (typeof source === 'string') {
445+
return source;
446+
} else if (source && typeof source === 'object') {
447+
return source.url || (source.toString && source.toString() !== '[object Object]' ? source.toString() : '');
448+
}
449+
return '';
450+
}).filter(url => url), // Filter out empty strings
418451
'markdown'
419452
);
420453

0 commit comments

Comments
 (0)