local-deep-research 0.4.4__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- local_deep_research/__init__.py +7 -0
- local_deep_research/__version__.py +1 -1
- local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
- local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
- local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
- local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
- local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
- local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
- local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
- local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
- local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
- local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
- local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
- local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
- local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
- local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
- local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
- local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
- local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
- local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
- local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
- local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
- local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
- local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
- local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
- local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
- local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
- local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
- local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
- local_deep_research/advanced_search_system/findings/repository.py +54 -17
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
- local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
- local_deep_research/advanced_search_system/questions/__init__.py +16 -0
- local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
- local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
- local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
- local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
- local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
- local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
- local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
- local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
- local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
- local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
- local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
- local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
- local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
- local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
- local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
- local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
- local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
- local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
- local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
- local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
- local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
- local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
- local_deep_research/api/benchmark_functions.py +6 -2
- local_deep_research/api/research_functions.py +10 -4
- local_deep_research/benchmarks/__init__.py +9 -7
- local_deep_research/benchmarks/benchmark_functions.py +6 -2
- local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
- local_deep_research/benchmarks/cli.py +38 -13
- local_deep_research/benchmarks/comparison/__init__.py +4 -2
- local_deep_research/benchmarks/comparison/evaluator.py +316 -239
- local_deep_research/benchmarks/datasets/__init__.py +1 -1
- local_deep_research/benchmarks/datasets/base.py +91 -72
- local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
- local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
- local_deep_research/benchmarks/datasets/utils.py +48 -29
- local_deep_research/benchmarks/datasets.py +4 -11
- local_deep_research/benchmarks/efficiency/__init__.py +8 -4
- local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
- local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
- local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
- local_deep_research/benchmarks/evaluators/composite.py +6 -2
- local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
- local_deep_research/benchmarks/graders.py +32 -10
- local_deep_research/benchmarks/metrics/README.md +1 -1
- local_deep_research/benchmarks/metrics/calculation.py +25 -10
- local_deep_research/benchmarks/metrics/reporting.py +7 -3
- local_deep_research/benchmarks/metrics/visualization.py +42 -23
- local_deep_research/benchmarks/metrics.py +1 -1
- local_deep_research/benchmarks/optimization/__init__.py +3 -1
- local_deep_research/benchmarks/optimization/api.py +7 -1
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
- local_deep_research/benchmarks/runners.py +48 -15
- local_deep_research/citation_handler.py +65 -92
- local_deep_research/citation_handlers/__init__.py +15 -0
- local_deep_research/citation_handlers/base_citation_handler.py +70 -0
- local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
- local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
- local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
- local_deep_research/config/llm_config.py +271 -169
- local_deep_research/config/search_config.py +14 -5
- local_deep_research/defaults/__init__.py +0 -1
- local_deep_research/metrics/__init__.py +13 -0
- local_deep_research/metrics/database.py +58 -0
- local_deep_research/metrics/db_models.py +115 -0
- local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
- local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
- local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
- local_deep_research/metrics/migrate_research_ratings.py +31 -0
- local_deep_research/metrics/models.py +61 -0
- local_deep_research/metrics/pricing/__init__.py +12 -0
- local_deep_research/metrics/pricing/cost_calculator.py +237 -0
- local_deep_research/metrics/pricing/pricing_cache.py +143 -0
- local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
- local_deep_research/metrics/query_utils.py +51 -0
- local_deep_research/metrics/search_tracker.py +380 -0
- local_deep_research/metrics/token_counter.py +1078 -0
- local_deep_research/migrate_db.py +3 -1
- local_deep_research/report_generator.py +22 -8
- local_deep_research/search_system.py +390 -9
- local_deep_research/test_migration.py +15 -5
- local_deep_research/utilities/db_utils.py +7 -4
- local_deep_research/utilities/es_utils.py +115 -104
- local_deep_research/utilities/llm_utils.py +15 -5
- local_deep_research/utilities/log_utils.py +151 -0
- local_deep_research/utilities/search_cache.py +387 -0
- local_deep_research/utilities/search_utilities.py +14 -6
- local_deep_research/utilities/threading_utils.py +92 -0
- local_deep_research/utilities/url_utils.py +6 -0
- local_deep_research/web/api.py +347 -0
- local_deep_research/web/app.py +13 -17
- local_deep_research/web/app_factory.py +71 -66
- local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
- local_deep_research/web/database/migrations.py +20 -3
- local_deep_research/web/database/models.py +74 -25
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +63 -83
- local_deep_research/web/routes/api_routes.py +56 -22
- local_deep_research/web/routes/benchmark_routes.py +4 -1
- local_deep_research/web/routes/globals.py +22 -0
- local_deep_research/web/routes/history_routes.py +71 -46
- local_deep_research/web/routes/metrics_routes.py +1155 -0
- local_deep_research/web/routes/research_routes.py +192 -54
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +412 -251
- local_deep_research/web/services/resource_service.py +36 -11
- local_deep_research/web/services/settings_manager.py +55 -17
- local_deep_research/web/services/settings_service.py +12 -4
- local_deep_research/web/services/socket_service.py +295 -188
- local_deep_research/web/static/css/custom_dropdown.css +180 -0
- local_deep_research/web/static/css/styles.css +39 -1
- local_deep_research/web/static/js/components/detail.js +633 -267
- local_deep_research/web/static/js/components/details.js +751 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
- local_deep_research/web/static/js/components/fallback/ui.js +23 -23
- local_deep_research/web/static/js/components/history.js +76 -76
- local_deep_research/web/static/js/components/logpanel.js +61 -13
- local_deep_research/web/static/js/components/progress.js +13 -2
- local_deep_research/web/static/js/components/research.js +99 -12
- local_deep_research/web/static/js/components/results.js +239 -106
- local_deep_research/web/static/js/main.js +40 -40
- local_deep_research/web/static/js/services/audio.js +1 -1
- local_deep_research/web/static/js/services/formatting.js +11 -11
- local_deep_research/web/static/js/services/keyboard.js +157 -0
- local_deep_research/web/static/js/services/pdf.js +80 -80
- local_deep_research/web/static/sounds/README.md +1 -1
- local_deep_research/web/templates/base.html +1 -0
- local_deep_research/web/templates/components/log_panel.html +7 -1
- local_deep_research/web/templates/components/mobile_nav.html +1 -1
- local_deep_research/web/templates/components/sidebar.html +3 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
- local_deep_research/web/templates/pages/details.html +325 -24
- local_deep_research/web/templates/pages/history.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +1929 -0
- local_deep_research/web/templates/pages/progress.html +2 -2
- local_deep_research/web/templates/pages/research.html +53 -17
- local_deep_research/web/templates/pages/results.html +12 -1
- local_deep_research/web/templates/pages/star_reviews.html +803 -0
- local_deep_research/web/utils/formatters.py +9 -3
- local_deep_research/web_search_engines/default_search_engines.py +5 -3
- local_deep_research/web_search_engines/engines/full_search.py +8 -2
- local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
- local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
- local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
- local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
- local_deep_research/web_search_engines/search_engine_base.py +83 -35
- local_deep_research/web_search_engines/search_engine_factory.py +25 -8
- local_deep_research/web_search_engines/search_engines_config.py +9 -3
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/METADATA +7 -1
- local_deep_research-0.5.2.dist-info/RECORD +265 -0
- local_deep_research-0.4.4.dist-info/RECORD +0 -177
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -15,15 +15,15 @@ async function generatePdf(title, content, metadata = {}) {
|
|
15
15
|
if (typeof jspdf === 'undefined') {
|
16
16
|
throw new Error('PDF generation libraries not loaded (jsPDF missing)');
|
17
17
|
}
|
18
|
-
|
18
|
+
|
19
19
|
if (typeof html2canvas === 'undefined') {
|
20
20
|
throw new Error('PDF generation libraries not loaded (html2canvas missing)');
|
21
21
|
}
|
22
|
-
|
22
|
+
|
23
23
|
// Ensure title and content are strings
|
24
24
|
title = String(title || 'Research Report');
|
25
25
|
content = String(content || '');
|
26
|
-
|
26
|
+
|
27
27
|
// Create a temporary container to render the markdown
|
28
28
|
const tempContainer = document.createElement('div');
|
29
29
|
tempContainer.style.position = 'absolute';
|
@@ -31,7 +31,7 @@ async function generatePdf(title, content, metadata = {}) {
|
|
31
31
|
tempContainer.style.top = '-9999px';
|
32
32
|
tempContainer.style.width = '8.5in'; // US Letter width
|
33
33
|
tempContainer.className = 'pdf-content';
|
34
|
-
|
34
|
+
|
35
35
|
// Add PDF-specific styles
|
36
36
|
tempContainer.innerHTML = `
|
37
37
|
<style>
|
@@ -144,23 +144,23 @@ async function generatePdf(title, content, metadata = {}) {
|
|
144
144
|
<p>Generated by Deep Research System</p>
|
145
145
|
</div>
|
146
146
|
`;
|
147
|
-
|
147
|
+
|
148
148
|
// Add to the document body temporarily
|
149
149
|
document.body.appendChild(tempContainer);
|
150
|
-
|
150
|
+
|
151
151
|
try {
|
152
152
|
console.log("Starting PDF generation...");
|
153
|
-
|
153
|
+
|
154
154
|
// Use the jsPDF library from the window object
|
155
155
|
const { jsPDF } = window.jspdf;
|
156
|
-
|
156
|
+
|
157
157
|
// Create a new PDF document
|
158
158
|
const pdf = new jsPDF('p', 'pt', 'letter');
|
159
159
|
const pdfWidth = pdf.internal.pageSize.getWidth();
|
160
160
|
const pdfHeight = pdf.internal.pageSize.getHeight();
|
161
161
|
const margin = 40;
|
162
162
|
const contentWidth = pdfWidth - 2 * margin;
|
163
|
-
|
163
|
+
|
164
164
|
// Function to add page with header
|
165
165
|
const addPageWithHeader = (pageNum) => {
|
166
166
|
if (pageNum > 1) {
|
@@ -170,49 +170,49 @@ async function generatePdf(title, content, metadata = {}) {
|
|
170
170
|
pdf.setTextColor(100, 100, 100);
|
171
171
|
pdf.text(`Deep Research - ${title} - Page ${pageNum}`, margin, pdfHeight - 20);
|
172
172
|
};
|
173
|
-
|
173
|
+
|
174
174
|
// Process each element with a more optimized approach
|
175
175
|
const contentDiv = tempContainer.querySelector('.pdf-body');
|
176
176
|
if (!contentDiv) {
|
177
177
|
throw new Error('PDF body container not found');
|
178
178
|
}
|
179
|
-
|
179
|
+
|
180
180
|
// Create a more efficient PDF generation approach that keeps text selectable
|
181
181
|
const elements = Array.from(contentDiv.children);
|
182
182
|
let currentY = margin;
|
183
183
|
let pageNum = 1;
|
184
|
-
|
184
|
+
|
185
185
|
// Add the first page with header
|
186
186
|
addPageWithHeader(pageNum);
|
187
|
-
|
187
|
+
|
188
188
|
// Process each element
|
189
189
|
for (const element of elements) {
|
190
190
|
// Skip style element
|
191
191
|
if (element.tagName === 'STYLE') continue;
|
192
|
-
|
192
|
+
|
193
193
|
try {
|
194
194
|
// Simple text content - handled directly by jsPDF for better text selection
|
195
|
-
if ((element.tagName === 'P' || element.tagName === 'DIV' || element.tagName === 'H1' ||
|
196
|
-
element.tagName === 'H2' || element.tagName === 'H3') &&
|
195
|
+
if ((element.tagName === 'P' || element.tagName === 'DIV' || element.tagName === 'H1' ||
|
196
|
+
element.tagName === 'H2' || element.tagName === 'H3') &&
|
197
197
|
!element.querySelector('img, canvas, svg, table') &&
|
198
198
|
!element.innerHTML.includes('<')) {
|
199
|
-
|
199
|
+
|
200
200
|
pdf.setFont('helvetica', element.tagName.startsWith('H') ? 'bold' : 'normal');
|
201
201
|
pdf.setFontSize(element.tagName === 'H1' ? 18 : (element.tagName === 'H2' ? 16 : (element.tagName === 'H3' ? 14 : 11)));
|
202
202
|
pdf.setTextColor(0, 0, 0);
|
203
|
-
|
203
|
+
|
204
204
|
const text = element.textContent.trim();
|
205
205
|
if (!text) continue; // Skip empty text
|
206
|
-
|
206
|
+
|
207
207
|
const textLines = pdf.splitTextToSize(text, contentWidth);
|
208
|
-
|
208
|
+
|
209
209
|
// Check if we need a new page
|
210
210
|
if (currentY + (textLines.length * 14) > pdfHeight - margin) {
|
211
211
|
pageNum++;
|
212
212
|
addPageWithHeader(pageNum);
|
213
213
|
currentY = margin;
|
214
214
|
}
|
215
|
-
|
215
|
+
|
216
216
|
pdf.text(textLines, margin, currentY + 12);
|
217
217
|
currentY += (textLines.length * 14) + 10;
|
218
218
|
}
|
@@ -223,22 +223,22 @@ async function generatePdf(title, content, metadata = {}) {
|
|
223
223
|
const item = listItems[i];
|
224
224
|
const itemText = item.textContent.trim();
|
225
225
|
if (!itemText) continue;
|
226
|
-
|
226
|
+
|
227
227
|
pdf.setFont('helvetica', 'normal');
|
228
228
|
pdf.setFontSize(11);
|
229
229
|
pdf.setTextColor(0, 0, 0);
|
230
|
-
|
230
|
+
|
231
231
|
const bulletPoint = element.tagName === 'UL' ? '•' : `${i + 1}.`;
|
232
232
|
const textWithBullet = `${bulletPoint} ${itemText}`;
|
233
233
|
const textLines = pdf.splitTextToSize(textWithBullet, contentWidth - 10);
|
234
|
-
|
234
|
+
|
235
235
|
// Check if we need a new page
|
236
236
|
if (currentY + (textLines.length * 14) > pdfHeight - margin) {
|
237
237
|
pageNum++;
|
238
238
|
addPageWithHeader(pageNum);
|
239
239
|
currentY = margin;
|
240
240
|
}
|
241
|
-
|
241
|
+
|
242
242
|
pdf.text(textLines, margin, currentY + 12);
|
243
243
|
currentY += (textLines.length * 14) + 5;
|
244
244
|
}
|
@@ -248,44 +248,44 @@ async function generatePdf(title, content, metadata = {}) {
|
|
248
248
|
else if (element.tagName === 'TABLE') {
|
249
249
|
const rows = Array.from(element.querySelectorAll('tr'));
|
250
250
|
if (rows.length === 0) continue;
|
251
|
-
|
251
|
+
|
252
252
|
// Get header cells
|
253
253
|
const headerCells = Array.from(rows[0].querySelectorAll('th, td'));
|
254
254
|
const colCount = headerCells.length || 1;
|
255
255
|
const colWidth = contentWidth / colCount;
|
256
|
-
|
256
|
+
|
257
257
|
// Start table at current position
|
258
258
|
let tableY = currentY;
|
259
|
-
|
259
|
+
|
260
260
|
// Check if we need a new page
|
261
261
|
if (tableY + 20 > pdfHeight - margin) {
|
262
262
|
pageNum++;
|
263
263
|
addPageWithHeader(pageNum);
|
264
264
|
tableY = margin;
|
265
265
|
}
|
266
|
-
|
266
|
+
|
267
267
|
// Draw header background
|
268
268
|
pdf.setFillColor(240, 240, 240);
|
269
269
|
pdf.rect(margin, tableY, contentWidth, 20, 'F');
|
270
|
-
|
270
|
+
|
271
271
|
// Draw header text
|
272
272
|
pdf.setFont("helvetica", "bold");
|
273
273
|
pdf.setFontSize(10);
|
274
274
|
pdf.setTextColor(0, 0, 0);
|
275
|
-
|
275
|
+
|
276
276
|
headerCells.forEach((cell, index) => {
|
277
277
|
const text = cell.textContent.trim();
|
278
278
|
const x = margin + (index * colWidth) + 5;
|
279
279
|
pdf.text(text, x, tableY + 13);
|
280
280
|
});
|
281
|
-
|
281
|
+
|
282
282
|
// Draw horizontal line after header
|
283
283
|
pdf.setDrawColor(200, 200, 200);
|
284
284
|
pdf.setLineWidth(0.5);
|
285
285
|
pdf.line(margin, tableY + 20, margin + contentWidth, tableY + 20);
|
286
|
-
|
286
|
+
|
287
287
|
tableY += 20;
|
288
|
-
|
288
|
+
|
289
289
|
// Draw table rows
|
290
290
|
pdf.setFont("helvetica", "normal");
|
291
291
|
for (let i = 1; i < rows.length; i++) {
|
@@ -294,46 +294,46 @@ async function generatePdf(title, content, metadata = {}) {
|
|
294
294
|
pageNum++;
|
295
295
|
addPageWithHeader(pageNum);
|
296
296
|
tableY = margin;
|
297
|
-
|
297
|
+
|
298
298
|
// Redraw header on new page
|
299
299
|
pdf.setFillColor(240, 240, 240);
|
300
300
|
pdf.rect(margin, tableY, contentWidth, 20, 'F');
|
301
|
-
|
301
|
+
|
302
302
|
pdf.setFont("helvetica", "bold");
|
303
303
|
headerCells.forEach((cell, index) => {
|
304
304
|
const text = cell.textContent.trim();
|
305
305
|
const x = margin + (index * colWidth) + 5;
|
306
306
|
pdf.text(text, x, tableY + 13);
|
307
307
|
});
|
308
|
-
|
308
|
+
|
309
309
|
pdf.line(margin, tableY + 20, margin + contentWidth, tableY + 20);
|
310
310
|
tableY += 20;
|
311
311
|
pdf.setFont("helvetica", "normal");
|
312
312
|
}
|
313
|
-
|
313
|
+
|
314
314
|
// Get cells for this row
|
315
315
|
const cells = Array.from(rows[i].querySelectorAll('td, th'));
|
316
|
-
|
316
|
+
|
317
317
|
// Draw cell content
|
318
318
|
cells.forEach((cell, index) => {
|
319
319
|
const text = cell.textContent.trim();
|
320
320
|
if (!text) return;
|
321
|
-
|
321
|
+
|
322
322
|
const x = margin + (index * colWidth) + 5;
|
323
323
|
pdf.text(text, x, tableY + 13);
|
324
324
|
});
|
325
|
-
|
325
|
+
|
326
326
|
// Draw horizontal line after row
|
327
327
|
pdf.line(margin, tableY + 20, margin + contentWidth, tableY + 20);
|
328
328
|
tableY += 20;
|
329
329
|
}
|
330
|
-
|
330
|
+
|
331
331
|
// Draw vertical lines
|
332
332
|
for (let i = 0; i <= colCount; i++) {
|
333
333
|
const x = margin + (i * colWidth);
|
334
334
|
pdf.line(x, currentY, x, tableY);
|
335
335
|
}
|
336
|
-
|
336
|
+
|
337
337
|
// Update current position to after the table
|
338
338
|
currentY = tableY + 10;
|
339
339
|
}
|
@@ -342,67 +342,67 @@ async function generatePdf(title, content, metadata = {}) {
|
|
342
342
|
const preElement = element.tagName === 'PRE' ? element : element.querySelector('pre');
|
343
343
|
const codeText = preElement.textContent.trim();
|
344
344
|
if (!codeText) continue;
|
345
|
-
|
345
|
+
|
346
346
|
pdf.setFont("courier", "normal"); // Use monospace font for code
|
347
347
|
pdf.setFontSize(9);
|
348
348
|
pdf.setTextColor(0, 0, 0);
|
349
|
-
|
349
|
+
|
350
350
|
// Split code into lines, respecting line breaks
|
351
351
|
const codeLines = codeText.split(/\r?\n/);
|
352
352
|
const wrappedLines = [];
|
353
|
-
|
353
|
+
|
354
354
|
codeLines.forEach(line => {
|
355
355
|
// Wrap long lines
|
356
356
|
const wrappedLine = pdf.splitTextToSize(line, contentWidth - 20);
|
357
357
|
wrappedLines.push(...wrappedLine);
|
358
358
|
});
|
359
|
-
|
359
|
+
|
360
360
|
// Calculate code block height
|
361
361
|
const lineHeight = 12;
|
362
362
|
const codeHeight = wrappedLines.length * lineHeight + 20; // 10px padding top and bottom
|
363
|
-
|
363
|
+
|
364
364
|
// Check if we need a new page
|
365
365
|
if (currentY + codeHeight > pdfHeight - margin) {
|
366
366
|
pageNum++;
|
367
367
|
addPageWithHeader(pageNum);
|
368
368
|
currentY = margin;
|
369
369
|
}
|
370
|
-
|
370
|
+
|
371
371
|
// Draw code block background
|
372
372
|
pdf.setFillColor(245, 245, 245);
|
373
373
|
pdf.rect(margin, currentY, contentWidth, codeHeight, 'F');
|
374
|
-
|
374
|
+
|
375
375
|
// Draw code content
|
376
376
|
pdf.setTextColor(0, 0, 0);
|
377
377
|
wrappedLines.forEach((line, index) => {
|
378
378
|
pdf.text(line, margin + 10, currentY + 15 + (index * lineHeight));
|
379
379
|
});
|
380
|
-
|
380
|
+
|
381
381
|
// Update position
|
382
382
|
currentY += codeHeight + 10;
|
383
383
|
}
|
384
384
|
// Images - render as images
|
385
385
|
else if (element.tagName === 'IMG' || element.querySelector('img')) {
|
386
386
|
const imgElement = element.tagName === 'IMG' ? element : element.querySelector('img');
|
387
|
-
|
387
|
+
|
388
388
|
if (!imgElement || !imgElement.src) continue;
|
389
|
-
|
389
|
+
|
390
390
|
try {
|
391
391
|
// Create a new image to get dimensions
|
392
392
|
const img = new Image();
|
393
393
|
img.src = imgElement.src;
|
394
|
-
|
394
|
+
|
395
395
|
// Calculate dimensions
|
396
396
|
const imgWidth = contentWidth;
|
397
397
|
const imgHeight = img.height * (contentWidth / img.width);
|
398
|
-
|
398
|
+
|
399
399
|
// Check if we need a new page
|
400
400
|
if (currentY + imgHeight > pdfHeight - margin) {
|
401
401
|
pageNum++;
|
402
402
|
addPageWithHeader(pageNum);
|
403
403
|
currentY = margin;
|
404
404
|
}
|
405
|
-
|
405
|
+
|
406
406
|
// Add image to PDF
|
407
407
|
pdf.addImage(img.src, 'JPEG', margin, currentY, imgWidth, imgHeight);
|
408
408
|
currentY += imgHeight + 10;
|
@@ -416,21 +416,21 @@ async function generatePdf(title, content, metadata = {}) {
|
|
416
416
|
else {
|
417
417
|
// Try to extract text content and render it directly first
|
418
418
|
const textContent = element.textContent.trim();
|
419
|
-
|
419
|
+
|
420
420
|
if (textContent) {
|
421
421
|
pdf.setFont('helvetica', 'normal');
|
422
422
|
pdf.setFontSize(11);
|
423
423
|
pdf.setTextColor(0, 0, 0);
|
424
|
-
|
424
|
+
|
425
425
|
const textLines = pdf.splitTextToSize(textContent, contentWidth);
|
426
|
-
|
426
|
+
|
427
427
|
// Check if we need a new page
|
428
428
|
if (currentY + (textLines.length * 14) > pdfHeight - margin) {
|
429
429
|
pageNum++;
|
430
430
|
addPageWithHeader(pageNum);
|
431
431
|
currentY = margin;
|
432
432
|
}
|
433
|
-
|
433
|
+
|
434
434
|
pdf.text(textLines, margin, currentY + 12);
|
435
435
|
currentY += (textLines.length * 14) + 10;
|
436
436
|
} else {
|
@@ -442,17 +442,17 @@ async function generatePdf(title, content, metadata = {}) {
|
|
442
442
|
logging: false,
|
443
443
|
backgroundColor: '#FFFFFF'
|
444
444
|
});
|
445
|
-
|
445
|
+
|
446
446
|
const imgData = canvas.toDataURL('image/png');
|
447
447
|
const imgWidth = contentWidth;
|
448
448
|
const imgHeight = (canvas.height * contentWidth) / canvas.width;
|
449
|
-
|
449
|
+
|
450
450
|
if (currentY + imgHeight > pdfHeight - margin) {
|
451
451
|
pageNum++;
|
452
452
|
addPageWithHeader(pageNum);
|
453
453
|
currentY = margin;
|
454
454
|
}
|
455
|
-
|
455
|
+
|
456
456
|
pdf.addImage(imgData, 'PNG', margin, currentY, imgWidth, imgHeight);
|
457
457
|
currentY += imgHeight + 10;
|
458
458
|
} catch (canvasError) {
|
@@ -468,7 +468,7 @@ async function generatePdf(title, content, metadata = {}) {
|
|
468
468
|
currentY += 20;
|
469
469
|
}
|
470
470
|
}
|
471
|
-
|
471
|
+
|
472
472
|
// Generate the PDF blob
|
473
473
|
const blob = pdf.output('blob');
|
474
474
|
console.log("PDF generation completed successfully");
|
@@ -494,20 +494,20 @@ async function generatePdf(title, content, metadata = {}) {
|
|
494
494
|
async function downloadPdf(titleOrData, content, metadata = {}) {
|
495
495
|
try {
|
496
496
|
console.log("Starting PDF download process...");
|
497
|
-
|
497
|
+
|
498
498
|
let title, pdfContent, pdfMetadata;
|
499
|
-
|
499
|
+
|
500
500
|
// Determine if we're being passed a research data object or direct parameters
|
501
501
|
if (typeof titleOrData === 'object' && titleOrData !== null) {
|
502
502
|
// We were passed a research data object
|
503
503
|
const researchData = titleOrData;
|
504
504
|
const researchId = content; // Second parameter is research ID in this case
|
505
|
-
|
505
|
+
|
506
506
|
console.log("Processing research data object", { researchId });
|
507
|
-
|
507
|
+
|
508
508
|
// Extract title from research data
|
509
509
|
title = researchData.query || researchData.title || researchData.prompt || `Research ${researchId}`;
|
510
|
-
|
510
|
+
|
511
511
|
// Extract content - try all possible locations based on how data might be structured
|
512
512
|
if (researchData.markdown) {
|
513
513
|
pdfContent = researchData.markdown;
|
@@ -545,7 +545,7 @@ async function downloadPdf(titleOrData, content, metadata = {}) {
|
|
545
545
|
console.warn("Could not find content in research data, using JSON stringification");
|
546
546
|
pdfContent = JSON.stringify(researchData, null, 2);
|
547
547
|
}
|
548
|
-
|
548
|
+
|
549
549
|
// Extract metadata
|
550
550
|
pdfMetadata = {
|
551
551
|
mode: researchData.mode,
|
@@ -559,12 +559,12 @@ async function downloadPdf(titleOrData, content, metadata = {}) {
|
|
559
559
|
pdfContent = content || '';
|
560
560
|
pdfMetadata = metadata || {};
|
561
561
|
}
|
562
|
-
|
562
|
+
|
563
563
|
// Ensure title is a string
|
564
564
|
title = String(title || 'Research Report');
|
565
|
-
|
565
|
+
|
566
566
|
console.log("PDF parameters prepared", { titleLength: title.length, contentLength: pdfContent?.length });
|
567
|
-
|
567
|
+
|
568
568
|
// Show loading indicator
|
569
569
|
const loadingIndicator = document.createElement('div');
|
570
570
|
loadingIndicator.className = 'loading-indicator';
|
@@ -579,27 +579,27 @@ async function downloadPdf(titleOrData, content, metadata = {}) {
|
|
579
579
|
loadingIndicator.style.padding = '20px';
|
580
580
|
loadingIndicator.style.borderRadius = '5px';
|
581
581
|
document.body.appendChild(loadingIndicator);
|
582
|
-
|
582
|
+
|
583
583
|
// Generate the PDF
|
584
584
|
const blob = await generatePdf(title, pdfContent, pdfMetadata);
|
585
|
-
|
585
|
+
|
586
586
|
// Create a download link
|
587
587
|
const url = URL.createObjectURL(blob);
|
588
588
|
const downloadLink = document.createElement('a');
|
589
589
|
downloadLink.href = url;
|
590
|
-
|
590
|
+
|
591
591
|
// Generate a filename based on the title
|
592
592
|
const safeTitle = title.replace(/[^a-z0-9]/gi, '_').toLowerCase().substring(0, 30);
|
593
593
|
downloadLink.download = `${safeTitle}_research.pdf`;
|
594
|
-
|
594
|
+
|
595
595
|
// Trigger the download
|
596
596
|
document.body.appendChild(downloadLink);
|
597
597
|
downloadLink.click();
|
598
|
-
|
598
|
+
|
599
599
|
// Clean up
|
600
600
|
document.body.removeChild(downloadLink);
|
601
601
|
URL.revokeObjectURL(url);
|
602
|
-
|
602
|
+
|
603
603
|
console.log("PDF download process completed");
|
604
604
|
return true;
|
605
605
|
} catch (error) {
|
@@ -619,4 +619,4 @@ async function downloadPdf(titleOrData, content, metadata = {}) {
|
|
619
619
|
window.pdfService = {
|
620
620
|
generatePdf,
|
621
621
|
downloadPdf
|
622
|
-
};
|
622
|
+
};
|
@@ -26,4 +26,4 @@ You can download copyright-free sound files from these sources:
|
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
29
|
-
The application will automatically use these sounds when research tasks complete or fail, but only when the browser tab is not in focus.
|
29
|
+
The application will automatically use these sounds when research tasks complete or fail, but only when the browser tab is not in focus.
|
@@ -41,6 +41,7 @@
|
|
41
41
|
<script src="{{ url_for('research.serve_static', path='js/services/ui.js') }}"></script>
|
42
42
|
<script src="{{ url_for('research.serve_static', path='js/services/api.js') }}"></script>
|
43
43
|
<script src="{{ url_for('research.serve_static', path='js/services/socket.js') }}"></script>
|
44
|
+
<script src="{{ url_for('research.serve_static', path='js/services/keyboard.js') }}"></script>
|
44
45
|
|
45
46
|
<!-- Shared Components -->
|
46
47
|
<script src="{{ url_for('research.serve_static', path='js/components/logpanel.js') }}"></script>
|
@@ -11,9 +11,15 @@
|
|
11
11
|
<button class="small-btn selected" onclick="window.filterLogsByType('all')">All</button>
|
12
12
|
<button class="small-btn" onclick="window.filterLogsByType('milestone')">Milestones</button>
|
13
13
|
<button class="small-btn" onclick="window.filterLogsByType('info')">Info</button>
|
14
|
+
<button class="small-btn" onclick="window.filterLogsByType('warning')">Warning</button>
|
14
15
|
<button class="small-btn" onclick="window.filterLogsByType('error')">Errors</button>
|
15
16
|
</div>
|
16
17
|
</div>
|
18
|
+
<div class="log-download">
|
19
|
+
<button class="small-btn download-btn" id="log-download-button">
|
20
|
+
<i class="fas fa-download"></i> Download Logs
|
21
|
+
</button>
|
22
|
+
</div>
|
17
23
|
</div>
|
18
24
|
<div class="console-log" id="console-log-container">
|
19
25
|
<!-- Logs will be added here dynamically -->
|
@@ -29,4 +35,4 @@
|
|
29
35
|
<span class="log-badge"></span>
|
30
36
|
<span class="log-message"></span>
|
31
37
|
</div>
|
32
|
-
</template>
|
38
|
+
</template>
|
@@ -10,6 +10,9 @@
|
|
10
10
|
<li {% if active_page == 'history' %}class="active"{% endif %} data-page="history">
|
11
11
|
<a href="{{ url_for('research.history_page') }}"><i class="fas fa-history"></i> History</a>
|
12
12
|
</li>
|
13
|
+
<li {% if active_page == 'metrics' %}class="active"{% endif %} data-page="metrics">
|
14
|
+
<a href="{{ url_for('metrics.metrics_dashboard') }}"><i class="fas fa-chart-bar"></i> Metrics</a>
|
15
|
+
</li>
|
13
16
|
<li {% if active_page == 'settings' %}class="active"{% endif %} data-page="settings">
|
14
17
|
<a href="{{ url_for('research.settings_page') }}"><i class="fas fa-cog"></i> Settings</a>
|
15
18
|
</li>
|