local-deep-research 0.5.9__py3-none-any.whl → 0.6.0__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 (90) hide show
  1. local_deep_research/__version__.py +1 -1
  2. local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +11 -1
  3. local_deep_research/advanced_search_system/questions/browsecomp_question.py +32 -6
  4. local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +32 -8
  5. local_deep_research/advanced_search_system/strategies/source_based_strategy.py +2 -0
  6. local_deep_research/api/__init__.py +2 -0
  7. local_deep_research/api/research_functions.py +177 -3
  8. local_deep_research/benchmarks/graders.py +150 -5
  9. local_deep_research/benchmarks/models/__init__.py +19 -0
  10. local_deep_research/benchmarks/models/benchmark_models.py +283 -0
  11. local_deep_research/benchmarks/ui/__init__.py +1 -0
  12. local_deep_research/benchmarks/web_api/__init__.py +6 -0
  13. local_deep_research/benchmarks/web_api/benchmark_routes.py +862 -0
  14. local_deep_research/benchmarks/web_api/benchmark_service.py +920 -0
  15. local_deep_research/config/llm_config.py +106 -21
  16. local_deep_research/defaults/default_settings.json +447 -2
  17. local_deep_research/error_handling/report_generator.py +10 -0
  18. local_deep_research/llm/__init__.py +19 -0
  19. local_deep_research/llm/llm_registry.py +155 -0
  20. local_deep_research/metrics/db_models.py +3 -7
  21. local_deep_research/metrics/search_tracker.py +25 -11
  22. local_deep_research/search_system.py +12 -9
  23. local_deep_research/utilities/log_utils.py +23 -10
  24. local_deep_research/utilities/thread_context.py +99 -0
  25. local_deep_research/web/app_factory.py +32 -8
  26. local_deep_research/web/database/benchmark_schema.py +230 -0
  27. local_deep_research/web/database/convert_research_id_to_string.py +161 -0
  28. local_deep_research/web/database/models.py +55 -1
  29. local_deep_research/web/database/schema_upgrade.py +397 -2
  30. local_deep_research/web/database/uuid_migration.py +265 -0
  31. local_deep_research/web/routes/api_routes.py +62 -31
  32. local_deep_research/web/routes/history_routes.py +13 -6
  33. local_deep_research/web/routes/metrics_routes.py +264 -4
  34. local_deep_research/web/routes/research_routes.py +45 -18
  35. local_deep_research/web/routes/route_registry.py +352 -0
  36. local_deep_research/web/routes/settings_routes.py +382 -22
  37. local_deep_research/web/services/research_service.py +22 -29
  38. local_deep_research/web/services/settings_manager.py +53 -0
  39. local_deep_research/web/services/settings_service.py +2 -0
  40. local_deep_research/web/static/css/styles.css +8 -0
  41. local_deep_research/web/static/js/components/detail.js +7 -14
  42. local_deep_research/web/static/js/components/details.js +8 -10
  43. local_deep_research/web/static/js/components/fallback/ui.js +4 -4
  44. local_deep_research/web/static/js/components/history.js +6 -6
  45. local_deep_research/web/static/js/components/logpanel.js +14 -11
  46. local_deep_research/web/static/js/components/progress.js +51 -46
  47. local_deep_research/web/static/js/components/research.js +250 -89
  48. local_deep_research/web/static/js/components/results.js +5 -7
  49. local_deep_research/web/static/js/components/settings.js +32 -26
  50. local_deep_research/web/static/js/components/settings_sync.js +24 -23
  51. local_deep_research/web/static/js/config/urls.js +285 -0
  52. local_deep_research/web/static/js/main.js +8 -8
  53. local_deep_research/web/static/js/research_form.js +267 -12
  54. local_deep_research/web/static/js/services/api.js +18 -18
  55. local_deep_research/web/static/js/services/keyboard.js +8 -8
  56. local_deep_research/web/static/js/services/socket.js +53 -35
  57. local_deep_research/web/static/js/services/ui.js +1 -1
  58. local_deep_research/web/templates/base.html +4 -1
  59. local_deep_research/web/templates/components/custom_dropdown.html +5 -3
  60. local_deep_research/web/templates/components/mobile_nav.html +3 -3
  61. local_deep_research/web/templates/components/sidebar.html +9 -3
  62. local_deep_research/web/templates/pages/benchmark.html +2697 -0
  63. local_deep_research/web/templates/pages/benchmark_results.html +1136 -0
  64. local_deep_research/web/templates/pages/benchmark_simple.html +453 -0
  65. local_deep_research/web/templates/pages/cost_analytics.html +1 -1
  66. local_deep_research/web/templates/pages/metrics.html +212 -39
  67. local_deep_research/web/templates/pages/research.html +8 -6
  68. local_deep_research/web/templates/pages/star_reviews.html +1 -1
  69. local_deep_research/web_search_engines/engines/search_engine_arxiv.py +14 -1
  70. local_deep_research/web_search_engines/engines/search_engine_brave.py +15 -1
  71. local_deep_research/web_search_engines/engines/search_engine_ddg.py +20 -1
  72. local_deep_research/web_search_engines/engines/search_engine_google_pse.py +26 -2
  73. local_deep_research/web_search_engines/engines/search_engine_pubmed.py +15 -1
  74. local_deep_research/web_search_engines/engines/search_engine_retriever.py +192 -0
  75. local_deep_research/web_search_engines/engines/search_engine_tavily.py +307 -0
  76. local_deep_research/web_search_engines/rate_limiting/__init__.py +14 -0
  77. local_deep_research/web_search_engines/rate_limiting/__main__.py +9 -0
  78. local_deep_research/web_search_engines/rate_limiting/cli.py +209 -0
  79. local_deep_research/web_search_engines/rate_limiting/exceptions.py +21 -0
  80. local_deep_research/web_search_engines/rate_limiting/tracker.py +506 -0
  81. local_deep_research/web_search_engines/retriever_registry.py +108 -0
  82. local_deep_research/web_search_engines/search_engine_base.py +161 -43
  83. local_deep_research/web_search_engines/search_engine_factory.py +14 -0
  84. local_deep_research/web_search_engines/search_engines_config.py +20 -0
  85. local_deep_research-0.6.0.dist-info/METADATA +374 -0
  86. {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/RECORD +89 -64
  87. local_deep_research-0.5.9.dist-info/METADATA +0 -420
  88. {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/WHEEL +0 -0
  89. {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/entry_points.txt +0 -0
  90. {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -63,14 +63,7 @@
63
63
  * @returns {string|null} Research ID or null if not found
64
64
  */
65
65
  function getResearchIdFromUrl() {
66
- const pathParts = window.location.pathname.split('/');
67
- const detailsIndex = pathParts.indexOf('details');
68
-
69
- if (detailsIndex > 0 && detailsIndex + 1 < pathParts.length) {
70
- return pathParts[detailsIndex + 1];
71
- }
72
-
73
- return null;
66
+ return URLBuilder.extractResearchIdFromPattern('details');
74
67
  }
75
68
 
76
69
  /**
@@ -80,14 +73,14 @@
80
73
  const viewResultsButton = document.getElementById('view-results-btn');
81
74
  if (viewResultsButton) {
82
75
  viewResultsButton.addEventListener('click', function() {
83
- window.location.href = `/research/results/${currentResearchId}`;
76
+ window.location.href = URLBuilder.resultsPage(currentResearchId);
84
77
  });
85
78
  }
86
79
 
87
80
  const backButton = document.getElementById('back-to-history-from-details');
88
81
  if (backButton) {
89
82
  backButton.addEventListener('click', function() {
90
- window.location.href = '/research/history';
83
+ window.location.href = URLS.PAGES.HISTORY;
91
84
  });
92
85
  }
93
86
  }
@@ -99,7 +92,7 @@
99
92
  console.log('Loading basic research details...');
100
93
 
101
94
  try {
102
- const response = await fetch(`/research/api/research/${currentResearchId}`, {
95
+ const response = await fetch(URLBuilder.researchDetails(currentResearchId), {
103
96
  headers: {
104
97
  'Accept': 'application/json'
105
98
  }
@@ -130,7 +123,7 @@
130
123
 
131
124
  try {
132
125
  // Fetch research details
133
- const response = await fetch(`/research/api/research/${currentResearchId}`, {
126
+ const response = await fetch(URLBuilder.researchDetails(currentResearchId), {
134
127
  headers: {
135
128
  'Accept': 'application/json'
136
129
  }
@@ -202,7 +195,7 @@
202
195
  console.log('Loading search metrics...');
203
196
 
204
197
  try {
205
- const response = await fetch(`/metrics/api/metrics/research/${currentResearchId}/search`, {
198
+ const response = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_SEARCH, currentResearchId), {
206
199
  headers: {
207
200
  'Accept': 'application/json'
208
201
  }
@@ -354,7 +347,7 @@
354
347
  console.log('Loading token metrics...');
355
348
 
356
349
  try {
357
- const response = await fetch(`/metrics/api/metrics/research/${currentResearchId}/timeline`, {
350
+ const response = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_TIMELINE, currentResearchId), {
358
351
  headers: {
359
352
  'Accept': 'application/json'
360
353
  }
@@ -28,9 +28,7 @@
28
28
 
29
29
  // Get research ID from URL
30
30
  function getResearchIdFromUrl() {
31
- const path = window.location.pathname;
32
- const match = path.match(/\/research\/details\/(\d+)/);
33
- return match ? parseInt(match[1]) : null;
31
+ return URLBuilder.extractResearchIdFromPattern('details');
34
32
  }
35
33
 
36
34
  // Load research metrics data
@@ -45,7 +43,7 @@
45
43
 
46
44
  // Load research details (includes strategy)
47
45
  console.log('Fetching research details...');
48
- const detailsResponse = await fetch(`/history/details/${researchId}`);
46
+ const detailsResponse = await fetch(URLBuilder.historyDetails(researchId));
49
47
  console.log('Details response status:', detailsResponse.status);
50
48
 
51
49
  let researchDetails = null;
@@ -56,7 +54,7 @@
56
54
 
57
55
  // Load research metrics
58
56
  console.log('Fetching research metrics...');
59
- const metricsResponse = await fetch(`/metrics/api/metrics/research/${researchId}`);
57
+ const metricsResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH, researchId));
60
58
  console.log('Metrics response status:', metricsResponse.status);
61
59
 
62
60
  if (!metricsResponse.ok) {
@@ -80,7 +78,7 @@
80
78
 
81
79
  // Load timeline metrics
82
80
  console.log('Fetching timeline metrics...');
83
- const timelineResponse = await fetch(`/metrics/api/metrics/research/${researchId}/timeline`);
81
+ const timelineResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_TIMELINE, researchId));
84
82
  console.log('Timeline response status:', timelineResponse.status);
85
83
 
86
84
  let timelineData = null;
@@ -94,7 +92,7 @@
94
92
 
95
93
  // Load search metrics
96
94
  console.log('Fetching search metrics...');
97
- const searchResponse = await fetch(`/metrics/api/metrics/research/${researchId}/search`);
95
+ const searchResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_SEARCH, researchId));
98
96
  console.log('Search response status:', searchResponse.status);
99
97
 
100
98
  let searchData = null;
@@ -662,7 +660,7 @@
662
660
  document.getElementById('total-cost').textContent = '-';
663
661
  return;
664
662
 
665
- const response = await fetch(`/metrics/api/research-costs/${researchId}`);
663
+ const response = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_COSTS, researchId));
666
664
  if (response.ok) {
667
665
  const data = await response.json();
668
666
  if (data.status === 'success') {
@@ -735,7 +733,7 @@
735
733
  const viewResultsBtn = document.getElementById('view-results-btn');
736
734
  if (viewResultsBtn) {
737
735
  viewResultsBtn.addEventListener('click', () => {
738
- window.location.href = `/research/results/${researchId}`;
736
+ window.location.href = URLBuilder.resultsPage(researchId);
739
737
  });
740
738
  }
741
739
 
@@ -743,7 +741,7 @@
743
741
  const backBtn = document.getElementById('back-to-history');
744
742
  if (backBtn) {
745
743
  backBtn.addEventListener('click', () => {
746
- window.location.href = '/research/history';
744
+ window.location.href = URLS.PAGES.HISTORY;
747
745
  });
748
746
  }
749
747
  });
@@ -183,16 +183,16 @@
183
183
  let iconPath;
184
184
  switch (status) {
185
185
  case 'active':
186
- iconPath = '/research/static/img/favicon-active.ico';
186
+ iconPath = '/static/img/favicon-active.ico';
187
187
  break;
188
188
  case 'complete':
189
- iconPath = '/research/static/img/favicon-complete.ico';
189
+ iconPath = '/static/img/favicon-complete.ico';
190
190
  break;
191
191
  case 'error':
192
- iconPath = '/research/static/img/favicon-error.ico';
192
+ iconPath = '/static/img/favicon-error.ico';
193
193
  break;
194
194
  default:
195
- iconPath = '/research/static/img/favicon.ico';
195
+ iconPath = '/static/img/favicon.ico';
196
196
  }
197
197
 
198
198
  // Add cache busting parameter to force reload
@@ -74,7 +74,7 @@
74
74
 
75
75
  // Fallback implementation
76
76
  try {
77
- const response = await fetch('/research/api/history');
77
+ const response = await fetch(URLS.API.HISTORY);
78
78
  if (!response.ok) {
79
79
  throw new Error(`API Error: ${response.status} ${response.statusText}`);
80
80
  }
@@ -92,7 +92,7 @@
92
92
 
93
93
  // Fallback implementation
94
94
  try {
95
- const response = await fetch(`/research/api/delete/${researchId}`, {
95
+ const response = await fetch(`/api/delete/${researchId}`, {
96
96
  method: 'DELETE'
97
97
  });
98
98
  if (!response.ok) {
@@ -112,7 +112,7 @@
112
112
 
113
113
  // Fallback implementation
114
114
  try {
115
- const response = await fetch('/research/api/clear_history', {
115
+ const response = await fetch(URLS.API.CLEAR_HISTORY, {
116
116
  method: 'POST',
117
117
  headers: {
118
118
  'Content-Type': 'application/json'
@@ -352,7 +352,7 @@
352
352
  if (viewBtn) {
353
353
  viewBtn.addEventListener('click', (e) => {
354
354
  e.stopPropagation(); // Prevent item click
355
- window.location.href = `/research/results/${item.id}`;
355
+ window.location.href = URLBuilder.resultsPage(item.id);
356
356
  });
357
357
  }
358
358
 
@@ -367,9 +367,9 @@
367
367
  // Add click event to the whole item
368
368
  itemEl.addEventListener('click', () => {
369
369
  if (item.status === 'completed') {
370
- window.location.href = `/research/results/${item.id}`;
370
+ window.location.href = URLBuilder.resultsPage(item.id);
371
371
  } else {
372
- window.location.href = `/research/progress/${item.id}`;
372
+ window.location.href = URLBuilder.progressPage(item.id);
373
373
  }
374
374
  });
375
375
 
@@ -43,8 +43,8 @@
43
43
  console.log('Initializing shared log panel, research ID:', researchId);
44
44
 
45
45
  // Check if we're on a research-specific page (progress, results)
46
- const isResearchPage = window.location.pathname.includes('/research/progress/') ||
47
- window.location.pathname.includes('/research/results/') ||
46
+ const isResearchPage = window.location.pathname.includes('/progress/') ||
47
+ window.location.pathname.includes('/results/') ||
48
48
  document.getElementById('research-progress') ||
49
49
  document.getElementById('research-results');
50
50
 
@@ -199,7 +199,7 @@
199
199
  });
200
200
 
201
201
  // Fetch the log count from the API and update the indicators
202
- fetch(`/research/api/log_count/${researchId}`)
202
+ fetch(`/history/log_count/${researchId}`)
203
203
  .then(response => response.json())
204
204
  .then(data => {
205
205
  console.log('Log count data:', data);
@@ -299,7 +299,7 @@
299
299
  */
300
300
  async function fetchLogsForResearch(researchId) {
301
301
  // Fetch logs from API
302
- const response = await fetch(`/research/api/logs/${researchId}`);
302
+ const response = await fetch(URLBuilder.researchLogs(researchId));
303
303
  return await response.json();
304
304
  }
305
305
 
@@ -407,11 +407,14 @@
407
407
  }
408
408
 
409
409
  // Standard logs array processing
410
- if (data && Array.isArray(data.logs)) {
411
- console.log(`Processing ${data.logs.length} standard logs`);
410
+ // Check if data is directly an array (new format) or has a logs property (old format)
411
+ const logsArray = Array.isArray(data) ? data : (data && data.logs);
412
+
413
+ if (logsArray && Array.isArray(logsArray)) {
414
+ console.log(`Processing ${logsArray.length} standard logs`);
412
415
 
413
416
  // Process each standard log
414
- data.logs.forEach(log => {
417
+ logsArray.forEach(log => {
415
418
  if (!log.timestamp && !log.time) return; // Skip invalid logs
416
419
 
417
420
  // Skip duplicates based on message content
@@ -437,7 +440,7 @@
437
440
  id: `${log.timestamp || log.time}-${hashString(log.message || log.content || '')}`,
438
441
  time: log.timestamp || log.time,
439
442
  message: log.message || log.content || 'No message',
440
- type: log.type || log.level || 'info',
443
+ type: log.log_type || log.type || log.level || 'info',
441
444
  metadata: log.metadata || {},
442
445
  source: 'standard_logs'
443
446
  };
@@ -972,7 +975,7 @@
972
975
 
973
976
  // Find research ID from URL if available
974
977
  let researchId = null;
975
- const urlMatch = window.location.pathname.match(/\/research\/(progress|results)\/(\d+)/);
978
+ const urlMatch = window.location.pathname.match(/\/(progress|results)\/(\d+)/);
976
979
  if (urlMatch && urlMatch[2]) {
977
980
  researchId = urlMatch[2];
978
981
  console.log('Found research ID in URL:', researchId);
@@ -982,8 +985,8 @@
982
985
  }
983
986
 
984
987
  // Check for research page elements
985
- const isResearchPage = window.location.pathname.includes('/research/progress/') ||
986
- window.location.pathname.includes('/research/results/') ||
988
+ const isResearchPage = window.location.pathname.includes('/progress/') ||
989
+ window.location.pathname.includes('/results/') ||
987
990
  document.getElementById('research-progress') ||
988
991
  document.getElementById('research-results');
989
992
 
@@ -35,7 +35,7 @@
35
35
  */
36
36
  function initializeProgress() {
37
37
  // Get research ID from URL or localStorage
38
- currentResearchId = getResearchIdFromUrl() || localStorage.getItem('currentResearchId');
38
+ currentResearchId = getResearchIdFromUrl(); // Only from URL, not localStorage
39
39
 
40
40
  if (!currentResearchId) {
41
41
  console.error('No research ID found');
@@ -105,7 +105,7 @@
105
105
  console.log('Progress component initialized for research ID:', currentResearchId);
106
106
 
107
107
  // Get notification preference
108
- notificationsEnabled = localStorage.getItem('notificationsEnabled') === 'true';
108
+ notificationsEnabled = true; // Default to enabled
109
109
 
110
110
  // Get initial research status
111
111
  getInitialStatus();
@@ -202,14 +202,7 @@
202
202
  * @returns {string|null} The research ID or null if not found
203
203
  */
204
204
  function getResearchIdFromUrl() {
205
- const pathParts = window.location.pathname.split('/');
206
- const idIndex = pathParts.indexOf('progress') + 1;
207
-
208
- if (idIndex > 0 && idIndex < pathParts.length) {
209
- return pathParts[idIndex];
210
- }
211
-
212
- return null;
205
+ return URLBuilder.extractResearchIdFromPattern('progress');
213
206
  }
214
207
 
215
208
  /**
@@ -284,6 +277,15 @@
284
277
  function handleProgressUpdate(data) {
285
278
  console.log('Received progress update:', data);
286
279
 
280
+ // Debug: Log if this is a log_entry update
281
+ if (data && data.log_entry) {
282
+ console.log('Progress update contains log_entry:', {
283
+ type: data.log_entry.type,
284
+ message: data.log_entry.message,
285
+ hasOtherFields: Object.keys(data).filter(k => k !== 'log_entry').length > 0
286
+ });
287
+ }
288
+
287
289
  if (!data) return;
288
290
 
289
291
  // Process progress_log if available and add to logs
@@ -310,9 +312,25 @@
310
312
  }
311
313
  }
312
314
 
313
- // Update progress UI
315
+ // Check if this is a milestone log that should update the current task
316
+ let milestoneTask = null;
317
+ if (data.log_entry && (data.log_entry.type === 'milestone' || data.log_entry.type === 'MILESTONE') && data.log_entry.message) {
318
+ // Milestone logs should always update the current task
319
+ milestoneTask = data.log_entry.message;
320
+ console.log('Milestone task detected:', milestoneTask);
321
+ }
322
+
323
+ // Update progress UI (but preserve milestone task)
314
324
  updateProgressUI(data);
315
325
 
326
+ // If we have a milestone task, make sure it's set after updateProgressUI
327
+ if (milestoneTask && currentTaskText) {
328
+ console.log('Setting milestone task:', milestoneTask);
329
+ currentTaskText.textContent = milestoneTask;
330
+ currentTaskText.dataset.lastMessage = milestoneTask;
331
+ currentTaskText.dataset.isMilestone = 'true';
332
+ }
333
+
316
334
  // Check if research is completed
317
335
  if (data.status === 'completed' || data.status === 'failed' || data.status === 'cancelled') {
318
336
  handleResearchCompletion(data);
@@ -320,29 +338,8 @@
320
338
 
321
339
  // Update the current query text if available
322
340
  const currentQueryEl = document.getElementById('current-query');
323
- if (currentQueryEl && localStorage.getItem('currentQuery')) {
324
- currentQueryEl.textContent = localStorage.getItem('currentQuery');
325
- }
326
-
327
- // Check for task message updates with better fallbacks
328
- let taskUpdated = false;
329
-
330
- if (data.task_message && data.task_message.trim() !== '') {
331
- // Direct task message is highest priority
332
- setCurrentTask(data.task_message);
333
- taskUpdated = true;
334
- } else if (data.current_task && data.current_task.trim() !== '') {
335
- // Then try current_task field
336
- setCurrentTask(data.current_task);
337
- taskUpdated = true;
338
- } else if (data.message && data.message.trim() !== '') {
339
- // Finally fall back to general message
340
- // But only if it's informative (not just a status update)
341
- const msg = data.message.toLowerCase();
342
- if (!msg.includes('in progress') && !msg.includes('status update')) {
343
- setCurrentTask(data.message);
344
- taskUpdated = true;
345
- }
341
+ if (currentQueryEl && data.query) {
342
+ currentQueryEl.textContent = data.query;
346
343
  }
347
344
 
348
345
  // If no task info was provided, leave the current task as is
@@ -457,7 +454,8 @@
457
454
  switch (data.status) {
458
455
  case 'in_progress':
459
456
  // Don't show "In Progress" at all in status text
460
- return; // Skip status update entirely for in_progress
457
+ formattedStatus = null; // Don't update status text for in_progress
458
+ break;
461
459
  case 'completed':
462
460
  formattedStatus = 'Completed';
463
461
  break;
@@ -510,19 +508,25 @@
510
508
 
511
509
  // Check various fields that might contain the current task message
512
510
  if (!taskMessage) {
513
- specificProgressMessage = true;
514
- if (data.current_task) {
511
+ // First check for milestone in log_entry
512
+ if (data.log_entry && data.log_entry.message && (data.log_entry.type === "milestone" || data.log_entry.type === "MILESTONE")) {
513
+ taskMessage = data.log_entry.message;
514
+ specificProgressMessage = true;
515
+ } else if (data.current_task) {
515
516
  taskMessage = data.current_task;
517
+ specificProgressMessage = true;
516
518
  } else if (data.message) {
517
519
  taskMessage = data.message;
520
+ specificProgressMessage = true;
518
521
  } else if (data.task) {
519
522
  taskMessage = data.task;
523
+ specificProgressMessage = true;
520
524
  } else if (data.step) {
521
525
  taskMessage = data.step;
526
+ specificProgressMessage = true;
522
527
  } else if (data.phase) {
523
528
  taskMessage = `Phase: ${data.phase}`;
524
- } else if (data.log_entry && data.log_entry.message && data.log_entry.type == "milestone") {
525
- taskMessage = data.log_entry.message;
529
+ specificProgressMessage = true;
526
530
  } else {
527
531
  specificProgressMessage = false;
528
532
  }
@@ -538,7 +542,8 @@
538
542
 
539
543
  // If no message but we have a status, generate a more descriptive message
540
544
  // BUT ONLY if we don't already have a meaningful message displayed
541
- if (!specificProgressMessage && data.status && (!currentTaskText.dataset.lastMessage || currentTaskText.textContent === 'In Progress')) {
545
+ if (!specificProgressMessage && data.status &&
546
+ (!currentTaskText.dataset.lastMessage || currentTaskText.textContent === 'In Progress')) {
542
547
  let statusMsg;
543
548
  switch (data.status) {
544
549
  case 'starting':
@@ -592,7 +597,7 @@
592
597
  }
593
598
 
594
599
  // Show notification if enabled
595
- if (data.status === 'completed' && localStorage.getItem('notificationsEnabled') === 'true') {
600
+ if (data.status === 'completed' && notificationsEnabled) {
596
601
  showNotification('Research Completed', 'Your research has been completed successfully.');
597
602
  }
598
603
 
@@ -622,7 +627,7 @@
622
627
  // Show view results button
623
628
  if (viewResultsButton) {
624
629
  viewResultsButton.style.display = 'inline-block';
625
- viewResultsButton.href = `/research/results/${currentResearchId}`;
630
+ viewResultsButton.href = URLBuilder.resultsPage(currentResearchId);
626
631
  }
627
632
 
628
633
  // Hide cancel button
@@ -634,7 +639,7 @@
634
639
  if (data.status === 'failed') {
635
640
  if (viewResultsButton) {
636
641
  viewResultsButton.textContent = 'View Error Report';
637
- viewResultsButton.href = `/research/results/${currentResearchId}`;
642
+ viewResultsButton.href = URLBuilder.resultsPage(currentResearchId);
638
643
  viewResultsButton.style.display = 'inline-block';
639
644
  }
640
645
  } else {
@@ -731,7 +736,7 @@
731
736
  try {
732
737
  const notification = new Notification(title, {
733
738
  body: message,
734
- icon: type === 'error' ? '/research/static/img/error-icon.png' : '/research/static/img/favicon.png'
739
+ icon: type === 'error' ? '/static/img/error-icon.png' : '/static/img/favicon.png'
735
740
  });
736
741
 
737
742
  // Auto-close after 10 seconds
@@ -746,7 +751,7 @@
746
751
  if (permission === 'granted') {
747
752
  new Notification(title, {
748
753
  body: message,
749
- icon: type === 'error' ? '/research/static/img/error-icon.png' : '/research/static/img/favicon.png'
754
+ icon: type === 'error' ? '/static/img/error-icon.png' : '/static/img/favicon.png'
750
755
  });
751
756
  }
752
757
  });
@@ -922,7 +927,7 @@
922
927
  // Show error report button
923
928
  if (viewResultsButton) {
924
929
  viewResultsButton.textContent = 'View Error Report';
925
- viewResultsButton.href = `/research/results/${currentResearchId}`;
930
+ viewResultsButton.href = URLBuilder.resultsPage(currentResearchId);
926
931
  viewResultsButton.style.display = 'inline-block';
927
932
  }
928
933