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.
Files changed (220) hide show
  1. local_deep_research/__init__.py +7 -0
  2. local_deep_research/__version__.py +1 -1
  3. local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
  4. local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
  5. local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
  6. local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
  7. local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
  8. local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
  9. local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
  10. local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
  11. local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
  12. local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
  13. local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
  14. local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
  15. local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
  16. local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
  17. local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
  18. local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
  19. local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
  20. local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
  21. local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
  22. local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
  23. local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
  24. local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
  25. local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
  26. local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
  27. local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
  28. local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
  29. local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
  30. local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
  31. local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
  32. local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
  33. local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
  34. local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
  35. local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
  36. local_deep_research/advanced_search_system/findings/repository.py +54 -17
  37. local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
  38. local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
  39. local_deep_research/advanced_search_system/questions/__init__.py +16 -0
  40. local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
  41. local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
  42. local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
  43. local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
  44. local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
  45. local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
  46. local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
  47. local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
  48. local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
  49. local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
  50. local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
  51. local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
  52. local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
  53. local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
  54. local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
  55. local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
  56. local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
  57. local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
  58. local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
  59. local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
  60. local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
  61. local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
  62. local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
  63. local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
  64. local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
  65. local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
  66. local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
  67. local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
  68. local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
  69. local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
  70. local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
  71. local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
  72. local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
  73. local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
  74. local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
  75. local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
  76. local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
  77. local_deep_research/api/benchmark_functions.py +6 -2
  78. local_deep_research/api/research_functions.py +10 -4
  79. local_deep_research/benchmarks/__init__.py +9 -7
  80. local_deep_research/benchmarks/benchmark_functions.py +6 -2
  81. local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
  82. local_deep_research/benchmarks/cli.py +38 -13
  83. local_deep_research/benchmarks/comparison/__init__.py +4 -2
  84. local_deep_research/benchmarks/comparison/evaluator.py +316 -239
  85. local_deep_research/benchmarks/datasets/__init__.py +1 -1
  86. local_deep_research/benchmarks/datasets/base.py +91 -72
  87. local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
  88. local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
  89. local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
  90. local_deep_research/benchmarks/datasets/utils.py +48 -29
  91. local_deep_research/benchmarks/datasets.py +4 -11
  92. local_deep_research/benchmarks/efficiency/__init__.py +8 -4
  93. local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
  94. local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
  95. local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
  96. local_deep_research/benchmarks/evaluators/composite.py +6 -2
  97. local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
  98. local_deep_research/benchmarks/graders.py +32 -10
  99. local_deep_research/benchmarks/metrics/README.md +1 -1
  100. local_deep_research/benchmarks/metrics/calculation.py +25 -10
  101. local_deep_research/benchmarks/metrics/reporting.py +7 -3
  102. local_deep_research/benchmarks/metrics/visualization.py +42 -23
  103. local_deep_research/benchmarks/metrics.py +1 -1
  104. local_deep_research/benchmarks/optimization/__init__.py +3 -1
  105. local_deep_research/benchmarks/optimization/api.py +7 -1
  106. local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
  107. local_deep_research/benchmarks/runners.py +48 -15
  108. local_deep_research/citation_handler.py +65 -92
  109. local_deep_research/citation_handlers/__init__.py +15 -0
  110. local_deep_research/citation_handlers/base_citation_handler.py +70 -0
  111. local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
  112. local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
  113. local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
  114. local_deep_research/config/llm_config.py +271 -169
  115. local_deep_research/config/search_config.py +14 -5
  116. local_deep_research/defaults/__init__.py +0 -1
  117. local_deep_research/metrics/__init__.py +13 -0
  118. local_deep_research/metrics/database.py +58 -0
  119. local_deep_research/metrics/db_models.py +115 -0
  120. local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
  121. local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
  122. local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
  123. local_deep_research/metrics/migrate_research_ratings.py +31 -0
  124. local_deep_research/metrics/models.py +61 -0
  125. local_deep_research/metrics/pricing/__init__.py +12 -0
  126. local_deep_research/metrics/pricing/cost_calculator.py +237 -0
  127. local_deep_research/metrics/pricing/pricing_cache.py +143 -0
  128. local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
  129. local_deep_research/metrics/query_utils.py +51 -0
  130. local_deep_research/metrics/search_tracker.py +380 -0
  131. local_deep_research/metrics/token_counter.py +1078 -0
  132. local_deep_research/migrate_db.py +3 -1
  133. local_deep_research/report_generator.py +22 -8
  134. local_deep_research/search_system.py +390 -9
  135. local_deep_research/test_migration.py +15 -5
  136. local_deep_research/utilities/db_utils.py +7 -4
  137. local_deep_research/utilities/es_utils.py +115 -104
  138. local_deep_research/utilities/llm_utils.py +15 -5
  139. local_deep_research/utilities/log_utils.py +151 -0
  140. local_deep_research/utilities/search_cache.py +387 -0
  141. local_deep_research/utilities/search_utilities.py +14 -6
  142. local_deep_research/utilities/threading_utils.py +92 -0
  143. local_deep_research/utilities/url_utils.py +6 -0
  144. local_deep_research/web/api.py +347 -0
  145. local_deep_research/web/app.py +13 -17
  146. local_deep_research/web/app_factory.py +71 -66
  147. local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
  148. local_deep_research/web/database/migrations.py +20 -3
  149. local_deep_research/web/database/models.py +74 -25
  150. local_deep_research/web/database/schema_upgrade.py +49 -29
  151. local_deep_research/web/models/database.py +63 -83
  152. local_deep_research/web/routes/api_routes.py +56 -22
  153. local_deep_research/web/routes/benchmark_routes.py +4 -1
  154. local_deep_research/web/routes/globals.py +22 -0
  155. local_deep_research/web/routes/history_routes.py +71 -46
  156. local_deep_research/web/routes/metrics_routes.py +1155 -0
  157. local_deep_research/web/routes/research_routes.py +192 -54
  158. local_deep_research/web/routes/settings_routes.py +156 -55
  159. local_deep_research/web/services/research_service.py +412 -251
  160. local_deep_research/web/services/resource_service.py +36 -11
  161. local_deep_research/web/services/settings_manager.py +55 -17
  162. local_deep_research/web/services/settings_service.py +12 -4
  163. local_deep_research/web/services/socket_service.py +295 -188
  164. local_deep_research/web/static/css/custom_dropdown.css +180 -0
  165. local_deep_research/web/static/css/styles.css +39 -1
  166. local_deep_research/web/static/js/components/detail.js +633 -267
  167. local_deep_research/web/static/js/components/details.js +751 -0
  168. local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
  169. local_deep_research/web/static/js/components/fallback/ui.js +23 -23
  170. local_deep_research/web/static/js/components/history.js +76 -76
  171. local_deep_research/web/static/js/components/logpanel.js +61 -13
  172. local_deep_research/web/static/js/components/progress.js +13 -2
  173. local_deep_research/web/static/js/components/research.js +99 -12
  174. local_deep_research/web/static/js/components/results.js +239 -106
  175. local_deep_research/web/static/js/main.js +40 -40
  176. local_deep_research/web/static/js/services/audio.js +1 -1
  177. local_deep_research/web/static/js/services/formatting.js +11 -11
  178. local_deep_research/web/static/js/services/keyboard.js +157 -0
  179. local_deep_research/web/static/js/services/pdf.js +80 -80
  180. local_deep_research/web/static/sounds/README.md +1 -1
  181. local_deep_research/web/templates/base.html +1 -0
  182. local_deep_research/web/templates/components/log_panel.html +7 -1
  183. local_deep_research/web/templates/components/mobile_nav.html +1 -1
  184. local_deep_research/web/templates/components/sidebar.html +3 -0
  185. local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
  186. local_deep_research/web/templates/pages/details.html +325 -24
  187. local_deep_research/web/templates/pages/history.html +1 -1
  188. local_deep_research/web/templates/pages/metrics.html +1929 -0
  189. local_deep_research/web/templates/pages/progress.html +2 -2
  190. local_deep_research/web/templates/pages/research.html +53 -17
  191. local_deep_research/web/templates/pages/results.html +12 -1
  192. local_deep_research/web/templates/pages/star_reviews.html +803 -0
  193. local_deep_research/web/utils/formatters.py +9 -3
  194. local_deep_research/web_search_engines/default_search_engines.py +5 -3
  195. local_deep_research/web_search_engines/engines/full_search.py +8 -2
  196. local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
  197. local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
  198. local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
  199. local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
  200. local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
  201. local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
  202. local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
  203. local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
  204. local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
  205. local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
  206. local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
  207. local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
  208. local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
  209. local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
  210. local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
  211. local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
  212. local_deep_research/web_search_engines/search_engine_base.py +83 -35
  213. local_deep_research/web_search_engines/search_engine_factory.py +25 -8
  214. local_deep_research/web_search_engines/search_engines_config.py +9 -3
  215. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/METADATA +7 -1
  216. local_deep_research-0.5.2.dist-info/RECORD +265 -0
  217. local_deep_research-0.4.4.dist-info/RECORD +0 -177
  218. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/WHEEL +0 -0
  219. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
  220. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -8,18 +8,18 @@
8
8
  let searchInput = null;
9
9
  let clearHistoryBtn = null;
10
10
  let historyEmptyMessage = null;
11
-
11
+
12
12
  // Component state
13
13
  let historyItems = [];
14
14
  let filteredItems = [];
15
-
15
+
16
16
  // Fallback UI utilities in case main UI utils aren't loaded
17
17
  const uiUtils = {
18
18
  showSpinner: function(container, message) {
19
19
  if (window.ui && window.ui.showSpinner) {
20
20
  return window.ui.showSpinner(container, message);
21
21
  }
22
-
22
+
23
23
  // Fallback implementation
24
24
  if (!container) container = document.body;
25
25
  const spinnerHtml = `
@@ -30,12 +30,12 @@
30
30
  `;
31
31
  container.innerHTML = spinnerHtml;
32
32
  },
33
-
33
+
34
34
  hideSpinner: function(container) {
35
35
  if (window.ui && window.ui.hideSpinner) {
36
36
  return window.ui.hideSpinner(container);
37
37
  }
38
-
38
+
39
39
  // Fallback implementation
40
40
  if (!container) container = document.body;
41
41
  const spinner = container.querySelector('.loading-spinner');
@@ -43,35 +43,35 @@
43
43
  spinner.remove();
44
44
  }
45
45
  },
46
-
46
+
47
47
  showError: function(message) {
48
48
  if (window.ui && window.ui.showError) {
49
49
  return window.ui.showError(message);
50
50
  }
51
-
51
+
52
52
  // Fallback implementation
53
53
  console.error(message);
54
54
  alert(message);
55
55
  },
56
-
56
+
57
57
  showMessage: function(message) {
58
58
  if (window.ui && window.ui.showMessage) {
59
59
  return window.ui.showMessage(message);
60
60
  }
61
-
61
+
62
62
  // Fallback implementation
63
63
  console.log(message);
64
64
  alert(message);
65
65
  }
66
66
  };
67
-
67
+
68
68
  // Fallback API utilities
69
69
  const apiUtils = {
70
70
  getResearchHistory: async function() {
71
71
  if (window.api && window.api.getResearchHistory) {
72
72
  return window.api.getResearchHistory();
73
73
  }
74
-
74
+
75
75
  // Fallback implementation
76
76
  try {
77
77
  const response = await fetch('/research/api/history');
@@ -84,12 +84,12 @@
84
84
  throw error;
85
85
  }
86
86
  },
87
-
87
+
88
88
  deleteResearch: async function(researchId) {
89
89
  if (window.api && window.api.deleteResearch) {
90
90
  return window.api.deleteResearch(researchId);
91
91
  }
92
-
92
+
93
93
  // Fallback implementation
94
94
  try {
95
95
  const response = await fetch(`/research/api/delete/${researchId}`, {
@@ -104,12 +104,12 @@
104
104
  throw error;
105
105
  }
106
106
  },
107
-
107
+
108
108
  clearResearchHistory: async function() {
109
109
  if (window.api && window.api.clearResearchHistory) {
110
110
  return window.api.clearResearchHistory();
111
111
  }
112
-
112
+
113
113
  // Fallback implementation
114
114
  try {
115
115
  const response = await fetch('/research/api/clear_history', {
@@ -129,7 +129,7 @@
129
129
  }
130
130
  }
131
131
  };
132
-
132
+
133
133
  /**
134
134
  * Initialize the history component
135
135
  */
@@ -139,21 +139,21 @@
139
139
  searchInput = document.getElementById('history-search');
140
140
  clearHistoryBtn = document.getElementById('clear-history-btn');
141
141
  historyEmptyMessage = document.getElementById('history-empty-message');
142
-
142
+
143
143
  if (!historyContainer) {
144
144
  console.error('Required DOM elements not found for history component');
145
145
  return;
146
146
  }
147
-
147
+
148
148
  // Set up event listeners
149
149
  setupEventListeners();
150
-
150
+
151
151
  // Load history data
152
152
  loadHistoryData();
153
-
153
+
154
154
  console.log('History component initialized');
155
155
  }
156
-
156
+
157
157
  /**
158
158
  * Set up event listeners
159
159
  */
@@ -162,12 +162,12 @@
162
162
  if (searchInput) {
163
163
  searchInput.addEventListener('input', handleSearchInput);
164
164
  }
165
-
165
+
166
166
  // Clear history button
167
167
  if (clearHistoryBtn) {
168
168
  clearHistoryBtn.addEventListener('click', handleClearHistory);
169
169
  }
170
-
170
+
171
171
  // Delegation for history item clicks
172
172
  if (historyContainer) {
173
173
  historyContainer.addEventListener('click', function(e) {
@@ -179,22 +179,22 @@
179
179
  });
180
180
  }
181
181
  }
182
-
182
+
183
183
  /**
184
184
  * Load history data from API
185
185
  */
186
186
  async function loadHistoryData() {
187
187
  // Show loading state
188
188
  uiUtils.showSpinner(historyContainer, 'Loading research history...');
189
-
189
+
190
190
  try {
191
191
  // Get history items
192
192
  const response = await apiUtils.getResearchHistory();
193
-
193
+
194
194
  if (response && Array.isArray(response.items)) {
195
195
  historyItems = response.items;
196
196
  filteredItems = [...historyItems];
197
-
197
+
198
198
  // Render history items
199
199
  renderHistoryItems();
200
200
  } else {
@@ -206,17 +206,17 @@
206
206
  uiUtils.showError('Error loading history: ' + error.message);
207
207
  }
208
208
  }
209
-
209
+
210
210
  /**
211
211
  * Render history items
212
212
  */
213
213
  function renderHistoryItems() {
214
214
  // Hide spinner
215
215
  uiUtils.hideSpinner(historyContainer);
216
-
216
+
217
217
  // Clear container
218
218
  historyContainer.innerHTML = '';
219
-
219
+
220
220
  // Show empty message if no items
221
221
  if (filteredItems.length === 0) {
222
222
  if (historyEmptyMessage) {
@@ -230,30 +230,30 @@
230
230
  </div>
231
231
  `;
232
232
  }
233
-
233
+
234
234
  if (clearHistoryBtn) {
235
235
  clearHistoryBtn.style.display = 'none';
236
236
  }
237
237
  return;
238
238
  }
239
-
239
+
240
240
  // Hide empty message
241
241
  if (historyEmptyMessage) {
242
242
  historyEmptyMessage.style.display = 'none';
243
243
  }
244
-
244
+
245
245
  // Show clear button
246
246
  if (clearHistoryBtn) {
247
247
  clearHistoryBtn.style.display = 'inline-block';
248
248
  }
249
-
249
+
250
250
  // Create items
251
251
  filteredItems.forEach(item => {
252
252
  const itemElement = createHistoryItemElement(item);
253
253
  historyContainer.appendChild(itemElement);
254
254
  });
255
255
  }
256
-
256
+
257
257
  /**
258
258
  * Format date safely using the formatter if available
259
259
  */
@@ -261,7 +261,7 @@
261
261
  if (window.formatting && window.formatting.formatDate) {
262
262
  return window.formatting.formatDate(dateStr);
263
263
  }
264
-
264
+
265
265
  // Simple fallback date formatting
266
266
  try {
267
267
  const date = new Date(dateStr);
@@ -270,7 +270,7 @@
270
270
  return dateStr;
271
271
  }
272
272
  }
273
-
273
+
274
274
  /**
275
275
  * Format status safely using the formatter if available
276
276
  */
@@ -278,7 +278,7 @@
278
278
  if (window.formatting && window.formatting.formatStatus) {
279
279
  return window.formatting.formatStatus(status);
280
280
  }
281
-
281
+
282
282
  // Simple fallback formatting
283
283
  const statusMap = {
284
284
  'in_progress': 'In Progress',
@@ -286,10 +286,10 @@
286
286
  'failed': 'Failed',
287
287
  'suspended': 'Suspended'
288
288
  };
289
-
289
+
290
290
  return statusMap[status] || status;
291
291
  }
292
-
292
+
293
293
  /**
294
294
  * Format mode safely using the formatter if available
295
295
  */
@@ -297,16 +297,16 @@
297
297
  if (window.formatting && window.formatting.formatMode) {
298
298
  return window.formatting.formatMode(mode);
299
299
  }
300
-
300
+
301
301
  // Simple fallback formatting
302
302
  const modeMap = {
303
303
  'quick': 'Quick Summary',
304
304
  'detailed': 'Detailed Report'
305
305
  };
306
-
306
+
307
307
  return modeMap[mode] || mode;
308
308
  }
309
-
309
+
310
310
  /**
311
311
  * Create a history item element
312
312
  * @param {Object} item - The history item data
@@ -316,16 +316,16 @@
316
316
  const itemEl = document.createElement('div');
317
317
  itemEl.className = 'history-item';
318
318
  itemEl.dataset.id = item.id;
319
-
319
+
320
320
  // Format date
321
321
  const formattedDate = formatDate(item.created_at);
322
-
322
+
323
323
  // Get a display title (use query if title is not available)
324
324
  const displayTitle = item.title || formatTitleFromQuery(item.query);
325
-
325
+
326
326
  // Status class - convert in_progress to in-progress for CSS
327
327
  const statusClass = item.status ? item.status.replace('_', '-') : '';
328
-
328
+
329
329
  // Create the HTML content
330
330
  itemEl.innerHTML = `
331
331
  <div class="history-item-header">
@@ -337,7 +337,7 @@
337
337
  <div class="history-item-mode">${formatMode(item.mode)}</div>
338
338
  </div>
339
339
  <div class="history-item-actions">
340
- ${item.status === 'completed' ?
340
+ ${item.status === 'completed' ?
341
341
  `<button class="btn btn-sm btn-outline view-btn">
342
342
  <i class="fas fa-eye"></i> View
343
343
  </button>` : ''}
@@ -346,7 +346,7 @@
346
346
  </button>
347
347
  </div>
348
348
  `;
349
-
349
+
350
350
  // Add event listeners
351
351
  const viewBtn = itemEl.querySelector('.view-btn');
352
352
  if (viewBtn) {
@@ -355,7 +355,7 @@
355
355
  window.location.href = `/research/results/${item.id}`;
356
356
  });
357
357
  }
358
-
358
+
359
359
  const deleteBtn = itemEl.querySelector('.delete-item-btn');
360
360
  if (deleteBtn) {
361
361
  deleteBtn.addEventListener('click', (e) => {
@@ -363,7 +363,7 @@
363
363
  handleDeleteItem(item.id);
364
364
  });
365
365
  }
366
-
366
+
367
367
  // Add click event to the whole item
368
368
  itemEl.addEventListener('click', () => {
369
369
  if (item.status === 'completed') {
@@ -372,10 +372,10 @@
372
372
  window.location.href = `/research/progress/${item.id}`;
373
373
  }
374
374
  });
375
-
375
+
376
376
  return itemEl;
377
377
  }
378
-
378
+
379
379
  /**
380
380
  * Format a title from a query string
381
381
  * Truncates long queries and adds ellipsis
@@ -384,21 +384,21 @@
384
384
  */
385
385
  function formatTitleFromQuery(query) {
386
386
  if (!query) return 'Untitled Research';
387
-
387
+
388
388
  // Truncate long queries
389
389
  if (query.length > 60) {
390
390
  return query.substring(0, 57) + '...';
391
391
  }
392
-
392
+
393
393
  return query;
394
394
  }
395
-
395
+
396
396
  /**
397
397
  * Handle search input
398
398
  */
399
399
  function handleSearchInput() {
400
400
  const searchTerm = searchInput.value.trim().toLowerCase();
401
-
401
+
402
402
  if (!searchTerm) {
403
403
  // Reset to show all items
404
404
  filteredItems = [...historyItems];
@@ -406,23 +406,23 @@
406
406
  // Filter items based on search term
407
407
  filteredItems = historyItems.filter(item => {
408
408
  // Search in title if available, otherwise in query
409
- const titleMatch = item.title ?
410
- item.title.toLowerCase().includes(searchTerm) :
409
+ const titleMatch = item.title ?
410
+ item.title.toLowerCase().includes(searchTerm) :
411
411
  false;
412
-
412
+
413
413
  // Always search in query
414
- const queryMatch = item.query ?
415
- item.query.toLowerCase().includes(searchTerm) :
414
+ const queryMatch = item.query ?
415
+ item.query.toLowerCase().includes(searchTerm) :
416
416
  false;
417
-
417
+
418
418
  return titleMatch || queryMatch;
419
419
  });
420
420
  }
421
-
421
+
422
422
  // Render filtered items
423
423
  renderHistoryItems();
424
424
  }
425
-
425
+
426
426
  /**
427
427
  * Handle delete item
428
428
  * @param {string} itemId - The item ID to delete
@@ -431,18 +431,18 @@
431
431
  if (!confirm('Are you sure you want to delete this research? This action cannot be undone.')) {
432
432
  return;
433
433
  }
434
-
434
+
435
435
  try {
436
436
  // Delete item via API
437
437
  await apiUtils.deleteResearch(itemId);
438
-
438
+
439
439
  // Remove from arrays
440
440
  historyItems = historyItems.filter(item => item.id != itemId);
441
441
  filteredItems = filteredItems.filter(item => item.id != itemId);
442
-
442
+
443
443
  // Show success message
444
444
  uiUtils.showMessage('Research deleted successfully');
445
-
445
+
446
446
  // Re-render history items
447
447
  renderHistoryItems();
448
448
  } catch (error) {
@@ -450,7 +450,7 @@
450
450
  uiUtils.showError('Error deleting research: ' + error.message);
451
451
  }
452
452
  }
453
-
453
+
454
454
  /**
455
455
  * Handle clear history
456
456
  */
@@ -458,18 +458,18 @@
458
458
  if (!confirm('Are you sure you want to clear all research history? This action cannot be undone.')) {
459
459
  return;
460
460
  }
461
-
461
+
462
462
  try {
463
463
  // Clear history via API
464
464
  await apiUtils.clearResearchHistory();
465
-
465
+
466
466
  // Clear arrays
467
467
  historyItems = [];
468
468
  filteredItems = [];
469
-
469
+
470
470
  // Show success message
471
471
  uiUtils.showMessage('Research history cleared successfully');
472
-
472
+
473
473
  // Re-render history items
474
474
  renderHistoryItems();
475
475
  } catch (error) {
@@ -477,11 +477,11 @@
477
477
  uiUtils.showError('Error clearing history: ' + error.message);
478
478
  }
479
479
  }
480
-
480
+
481
481
  // Initialize on DOM content loaded
482
482
  if (document.readyState === 'loading') {
483
483
  document.addEventListener('DOMContentLoaded', initializeHistory);
484
484
  } else {
485
485
  initializeHistory();
486
486
  }
487
- })();
487
+ })();
@@ -32,6 +32,12 @@
32
32
  // If the research ID has changed, we'll update our connection
33
33
  console.log('Research ID changed from', window._logPanelState.connectedResearchId, 'to', researchId);
34
34
  window._logPanelState.connectedResearchId = researchId;
35
+ } else {
36
+ // Add callback for log download button.
37
+ const downloadButton = document.getElementById('log-download-button');
38
+ if (downloadButton) {
39
+ downloadButton.addEventListener('click', downloadLogs);
40
+ }
35
41
  }
36
42
 
37
43
  console.log('Initializing shared log panel, research ID:', researchId);
@@ -191,6 +197,23 @@
191
197
  logIndicators.forEach(indicator => {
192
198
  indicator.textContent = '0';
193
199
  });
200
+
201
+ // Fetch the log count from the API and update the indicators
202
+ fetch(`/research/api/log_count/${researchId}`)
203
+ .then(response => response.json())
204
+ .then(data => {
205
+ console.log('Log count data:', data);
206
+ if (data && typeof data.total_logs === 'number') {
207
+ logIndicators.forEach(indicator => {
208
+ indicator.textContent = data.total_logs;
209
+ });
210
+ } else {
211
+ console.error('Invalid log count data received from API');
212
+ }
213
+ })
214
+ .catch(error => {
215
+ console.error('Error fetching log count:', error);
216
+ });
194
217
  } else {
195
218
  console.warn('No log indicators found for initialization');
196
219
  }
@@ -269,6 +292,17 @@
269
292
  console.log('Log panel initialized');
270
293
  }
271
294
 
295
+ /**
296
+ * @brief Fetches all the logs for a research instance from the API.
297
+ * @param researchId The ID of the research instance.
298
+ * @returns {Promise<any>} The logs.
299
+ */
300
+ async function fetchLogsForResearch(researchId) {
301
+ // Fetch logs from API
302
+ const response = await fetch(`/research/api/logs/${researchId}`);
303
+ return await response.json();
304
+ }
305
+
272
306
  /**
273
307
  * Load logs for a specific research
274
308
  * @param {string} researchId - The research ID to load logs for
@@ -283,10 +317,7 @@
283
317
 
284
318
  console.log('Loading logs for research ID:', researchId);
285
319
 
286
- // Fetch logs from API
287
- const response = await fetch(`/research/api/logs/${researchId}`);
288
- const data = await response.json();
289
-
320
+ const data = await fetchLogsForResearch(researchId);
290
321
  console.log('Logs API response:', data);
291
322
 
292
323
  // Initialize array to hold all logs from different sources
@@ -714,14 +745,6 @@
714
745
  logLevel = logEntry.type;
715
746
  } else if (logEntry.metadata && logEntry.metadata.type) {
716
747
  logLevel = logEntry.metadata.type;
717
- } else if (logEntry.metadata && logEntry.metadata.phase) {
718
- if (logEntry.metadata.phase === 'complete' ||
719
- logEntry.metadata.phase === 'iteration_complete' ||
720
- logEntry.metadata.phase === 'report_complete') {
721
- logLevel = 'milestone';
722
- } else if (logEntry.metadata.phase === 'error') {
723
- logLevel = 'error';
724
- }
725
748
  } else if (logEntry.level) {
726
749
  logLevel = logEntry.level;
727
750
  }
@@ -842,10 +865,13 @@
842
865
  case 'all':
843
866
  return true;
844
867
  case 'info':
845
- return logType === 'info';
868
+ return logType === 'info' || logType === 'warning' || logType === 'milestone' || logType === 'error';
846
869
  case 'milestone':
847
870
  case 'milestones': // Handle plural form too
848
871
  return logType === 'milestone';
872
+ case 'warning':
873
+ case 'warnings':
874
+ return logType === 'warning' || logType === 'error';
849
875
  case 'error':
850
876
  case 'errors': // Handle plural form too
851
877
  return logType === 'error';
@@ -910,6 +936,28 @@
910
936
  }
911
937
  }
912
938
 
939
+ /**
940
+ * @brief Handler for the log download button which downloads all the
941
+ * saved logs to the user's computer.
942
+ */
943
+ function downloadLogs() {
944
+ const researchId = window._logPanelState.connectedResearchId;
945
+ fetchLogsForResearch(researchId).then((logData) => {
946
+ // Create a blob with the logs data
947
+ const blob = new Blob([JSON.stringify(logData, null, 2)], { type: 'application/json' });
948
+
949
+ // Create a link element and trigger download
950
+ const url = URL.createObjectURL(blob);
951
+ const a = document.createElement('a');
952
+ a.href = url;
953
+ a.download = `research_logs_${researchId}.json`;
954
+ document.body.appendChild(a);
955
+ a.click();
956
+ document.body.removeChild(a);
957
+ URL.revokeObjectURL(url);
958
+ });
959
+ }
960
+
913
961
  // Expose public API
914
962
  window.logPanel = {
915
963
  initialize: initializeLogPanel,
@@ -8,6 +8,9 @@
8
8
  let pollInterval = null;
9
9
  let isCompleted = false;
10
10
  let socketErrorShown = false;
11
+ // Keeps track of whether we've set a specific progress message or just
12
+ // a generic one based on the status.
13
+ let specificProgressMessage = false;
11
14
 
12
15
  // DOM Elements
13
16
  let progressBar = null;
@@ -78,6 +81,9 @@
78
81
  cancelButton.addEventListener('click', handleCancelResearch);
79
82
  }
80
83
 
84
+ // Keyboard navigation is now handled by the global keyboard service
85
+ // The Enter key shortcut for viewing results is automatically registered
86
+
81
87
  // Note: Log panel is now automatically initialized by logpanel.js
82
88
  // No need to manually initialize it here
83
89
 
@@ -492,6 +498,7 @@
492
498
  for (let i = progressLogs.length - 1; i >= 0; i--) {
493
499
  if (progressLogs[i].message && progressLogs[i].message.trim() !== '') {
494
500
  taskMessage = progressLogs[i].message;
501
+ specificProgressMessage = true;
495
502
  break;
496
503
  }
497
504
  }
@@ -503,6 +510,7 @@
503
510
 
504
511
  // Check various fields that might contain the current task message
505
512
  if (!taskMessage) {
513
+ specificProgressMessage = true;
506
514
  if (data.current_task) {
507
515
  taskMessage = data.current_task;
508
516
  } else if (data.message) {
@@ -513,8 +521,10 @@
513
521
  taskMessage = data.step;
514
522
  } else if (data.phase) {
515
523
  taskMessage = `Phase: ${data.phase}`;
516
- } else if (data.log_entry && data.log_entry.message) {
524
+ } else if (data.log_entry && data.log_entry.message && data.log_entry.type == "milestone") {
517
525
  taskMessage = data.log_entry.message;
526
+ } else {
527
+ specificProgressMessage = false;
518
528
  }
519
529
  }
520
530
 
@@ -528,7 +538,7 @@
528
538
 
529
539
  // If no message but we have a status, generate a more descriptive message
530
540
  // BUT ONLY if we don't already have a meaningful message displayed
531
- if (!taskMessage && data.status && (!currentTaskText.dataset.lastMessage || currentTaskText.textContent === 'In Progress')) {
541
+ if (!specificProgressMessage && data.status && (!currentTaskText.dataset.lastMessage || currentTaskText.textContent === 'In Progress')) {
532
542
  let statusMsg;
533
543
  switch (data.status) {
534
544
  case 'starting':
@@ -641,6 +651,7 @@
641
651
  }
642
652
  }
643
653
 
654
+
644
655
  /**
645
656
  * Handle research cancellation
646
657
  */