local-deep-research 0.4.4__py3-none-any.whl → 0.5.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 (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 +5 -3
  149. local_deep_research/web/database/models.py +51 -2
  150. local_deep_research/web/database/schema_upgrade.py +49 -29
  151. local_deep_research/web/models/database.py +51 -61
  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 +227 -41
  158. local_deep_research/web/routes/settings_routes.py +156 -55
  159. local_deep_research/web/services/research_service.py +310 -103
  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.0.dist-info}/METADATA +7 -1
  216. local_deep_research-0.5.0.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.0.dist-info}/WHEEL +0 -0
  219. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/entry_points.txt +0 -0
  220. {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,751 @@
1
+ /**
2
+ * Research Details Page JavaScript
3
+ * Handles displaying detailed metrics for a specific research session
4
+ */
5
+ (function() {
6
+ 'use strict';
7
+
8
+ let researchId = null;
9
+ let metricsData = null;
10
+ let timelineChart = null;
11
+ let searchChart = null;
12
+
13
+ // Format large numbers with commas
14
+ function formatNumber(num) {
15
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
16
+ }
17
+
18
+ // Format currency
19
+ function formatCurrency(amount) {
20
+ if (amount < 0.01) {
21
+ return `$${amount.toFixed(6)}`;
22
+ } else if (amount < 1) {
23
+ return `$${amount.toFixed(4)}`;
24
+ } else {
25
+ return `$${amount.toFixed(2)}`;
26
+ }
27
+ }
28
+
29
+ // Get research ID from URL
30
+ function getResearchIdFromUrl() {
31
+ const path = window.location.pathname;
32
+ const match = path.match(/\/research\/details\/(\d+)/);
33
+ return match ? parseInt(match[1]) : null;
34
+ }
35
+
36
+ // Load research metrics data
37
+ async function loadResearchMetrics() {
38
+ try {
39
+ console.log('Loading research metrics for ID:', researchId);
40
+
41
+ // Show loading state
42
+ document.getElementById('loading').style.display = 'block';
43
+ document.getElementById('details-content').style.display = 'none';
44
+ document.getElementById('error').style.display = 'none';
45
+
46
+ // Load research details (includes strategy)
47
+ console.log('Fetching research details...');
48
+ const detailsResponse = await fetch(`/history/details/${researchId}`);
49
+ console.log('Details response status:', detailsResponse.status);
50
+
51
+ let researchDetails = null;
52
+ if (detailsResponse.ok) {
53
+ researchDetails = await detailsResponse.json();
54
+ console.log('Research details loaded:', researchDetails);
55
+ }
56
+
57
+ // Load research metrics
58
+ console.log('Fetching research metrics...');
59
+ const metricsResponse = await fetch(`/metrics/api/metrics/research/${researchId}`);
60
+ console.log('Metrics response status:', metricsResponse.status);
61
+
62
+ if (!metricsResponse.ok) {
63
+ throw new Error(`Metrics API failed: ${metricsResponse.status}`);
64
+ }
65
+
66
+ const metricsResult = await metricsResponse.json();
67
+ console.log('Metrics result:', metricsResult);
68
+
69
+ if (metricsResult.status !== 'success') {
70
+ throw new Error('Failed to load research metrics');
71
+ }
72
+
73
+ metricsData = metricsResult.metrics;
74
+ console.log('Metrics data loaded:', metricsData);
75
+
76
+ // Display research details first
77
+ if (researchDetails) {
78
+ displayResearchDetails(researchDetails);
79
+ }
80
+
81
+ // Load timeline metrics
82
+ console.log('Fetching timeline metrics...');
83
+ const timelineResponse = await fetch(`/metrics/api/metrics/research/${researchId}/timeline`);
84
+ console.log('Timeline response status:', timelineResponse.status);
85
+
86
+ let timelineData = null;
87
+ if (timelineResponse.ok) {
88
+ const timelineResult = await timelineResponse.json();
89
+ console.log('Timeline result:', timelineResult);
90
+ if (timelineResult.status === 'success') {
91
+ timelineData = timelineResult.metrics;
92
+ }
93
+ }
94
+
95
+ // Load search metrics
96
+ console.log('Fetching search metrics...');
97
+ const searchResponse = await fetch(`/metrics/api/metrics/research/${researchId}/search`);
98
+ console.log('Search response status:', searchResponse.status);
99
+
100
+ let searchData = null;
101
+ if (searchResponse.ok) {
102
+ const searchResult = await searchResponse.json();
103
+ console.log('Search result:', searchResult);
104
+ if (searchResult.status === 'success') {
105
+ searchData = searchResult.metrics;
106
+ }
107
+ }
108
+
109
+ // Display all data
110
+ console.log('Displaying research metrics...');
111
+ displayResearchMetrics();
112
+
113
+ if (timelineData) {
114
+ console.log('Displaying timeline metrics...');
115
+ displayTimelineMetrics(timelineData);
116
+
117
+ console.log('Chart.js available:', typeof Chart !== 'undefined');
118
+ console.log('Timeline data for chart:', timelineData);
119
+ createTimelineChart(timelineData);
120
+ }
121
+
122
+ if (searchData) {
123
+ console.log('Displaying search metrics...');
124
+ displaySearchMetrics(searchData);
125
+ createSearchChart(searchData);
126
+ }
127
+
128
+ // Load cost data
129
+ console.log('Loading cost data...');
130
+ loadCostData();
131
+
132
+ console.log('Showing details content...');
133
+ const loadingEl = document.getElementById('loading');
134
+ const contentEl = document.getElementById('details-content');
135
+ const errorEl = document.getElementById('error');
136
+
137
+ loadingEl.style.display = 'none';
138
+ errorEl.style.display = 'none';
139
+ contentEl.style.display = 'block';
140
+
141
+ // Show metrics sections
142
+ console.log('Showing metrics sections...');
143
+ const tokenMetricsSection = document.getElementById('token-metrics-section');
144
+ const searchMetricsSection = document.getElementById('search-metrics-section');
145
+
146
+ if (tokenMetricsSection) {
147
+ tokenMetricsSection.style.display = 'block';
148
+ console.log('Token metrics section shown');
149
+ }
150
+
151
+ if (searchMetricsSection) {
152
+ searchMetricsSection.style.display = 'block';
153
+ console.log('Search metrics section shown');
154
+ }
155
+
156
+ // Force visibility with CSS overrides
157
+ contentEl.style.visibility = 'visible';
158
+ contentEl.style.opacity = '1';
159
+ contentEl.style.position = 'relative';
160
+ contentEl.style.zIndex = '1000';
161
+
162
+ console.log('Loading display:', loadingEl.style.display);
163
+ console.log('Content display:', contentEl.style.display);
164
+ console.log('Error display:', errorEl.style.display);
165
+
166
+ // Verify content is actually populated
167
+ const totalTokensEl = document.getElementById('total-tokens');
168
+ const researchQueryEl = document.getElementById('research-query');
169
+ console.log('Total tokens value:', totalTokensEl ? totalTokensEl.textContent : 'ELEMENT NOT FOUND');
170
+ console.log('Research query value:', researchQueryEl ? researchQueryEl.textContent : 'ELEMENT NOT FOUND');
171
+ console.log('Content element height:', contentEl.offsetHeight);
172
+ console.log('Content element children:', contentEl.children.length);
173
+
174
+ } catch (error) {
175
+ console.error('Error loading research metrics:', error);
176
+ console.error('Error details:', error.message, error.stack);
177
+ showError();
178
+ }
179
+ }
180
+
181
+ // Display research details from history endpoint
182
+ function displayResearchDetails(details) {
183
+ console.log('displayResearchDetails called with:', details);
184
+
185
+ // Update basic research info
186
+ if (details.query) {
187
+ document.getElementById('research-query').textContent = details.query;
188
+ }
189
+ if (details.mode) {
190
+ document.getElementById('research-mode').textContent = details.mode;
191
+ }
192
+ if (details.created_at) {
193
+ const date = new Date(details.created_at);
194
+ document.getElementById('research-date').textContent = date.toLocaleString();
195
+ }
196
+
197
+ // Update strategy information
198
+ if (details.strategy) {
199
+ document.getElementById('research-strategy').textContent = details.strategy;
200
+ } else {
201
+ document.getElementById('research-strategy').textContent = 'Not recorded';
202
+ }
203
+
204
+ // Update progress
205
+ if (details.progress !== undefined) {
206
+ const progressFill = document.getElementById('detail-progress-fill');
207
+ const progressText = document.getElementById('detail-progress-percentage');
208
+ if (progressFill && progressText) {
209
+ progressFill.style.width = `${details.progress}%`;
210
+ progressText.textContent = `${details.progress}%`;
211
+ }
212
+ }
213
+ }
214
+
215
+ // Display basic research metrics
216
+ function displayResearchMetrics() {
217
+ console.log('displayResearchMetrics called with:', metricsData);
218
+ if (!metricsData) {
219
+ console.error('No metrics data available');
220
+ return;
221
+ }
222
+
223
+ // Update summary cards
224
+ const totalTokensEl = document.getElementById('total-tokens');
225
+ const totalTokens = formatNumber(metricsData.total_tokens || 0);
226
+ console.log('Setting total tokens to:', totalTokens);
227
+ totalTokensEl.textContent = totalTokens;
228
+
229
+ // Calculate prompt/completion tokens from model usage
230
+ let totalPromptTokens = 0;
231
+ let totalCompletionTokens = 0;
232
+ let totalCalls = 0;
233
+ let model = 'Unknown';
234
+
235
+ if (metricsData.model_usage && metricsData.model_usage.length > 0) {
236
+ metricsData.model_usage.forEach(usage => {
237
+ totalPromptTokens += usage.prompt_tokens || 0;
238
+ totalCompletionTokens += usage.completion_tokens || 0;
239
+ totalCalls += usage.calls || 0;
240
+ if (model === 'Unknown') {
241
+ model = usage.model || 'Unknown';
242
+ }
243
+ });
244
+ }
245
+
246
+ document.getElementById('prompt-tokens').textContent = formatNumber(totalPromptTokens);
247
+ document.getElementById('completion-tokens').textContent = formatNumber(totalCompletionTokens);
248
+ document.getElementById('llm-calls').textContent = formatNumber(metricsData.total_calls || totalCalls);
249
+
250
+ // Update model info
251
+ document.getElementById('model-used').textContent = model;
252
+
253
+ // Response time will be updated by timeline data
254
+ document.getElementById('avg-response-time').textContent = '0s';
255
+ }
256
+
257
+ // Display timeline metrics
258
+ function displayTimelineMetrics(timelineData) {
259
+ if (!timelineData) return;
260
+
261
+ // Update research info from timeline data
262
+ if (timelineData.research_details) {
263
+ const details = timelineData.research_details;
264
+ document.getElementById('research-query').textContent = details.query || 'Unknown';
265
+ document.getElementById('research-mode').textContent = details.mode || 'Unknown';
266
+ if (details.created_at) {
267
+ const date = new Date(details.created_at);
268
+ document.getElementById('research-date').textContent = date.toLocaleString();
269
+ }
270
+ }
271
+
272
+ // Update summary info
273
+ if (timelineData.summary) {
274
+ const summary = timelineData.summary;
275
+ const avgResponseTime = (summary.avg_response_time || 0) / 1000;
276
+ document.getElementById('avg-response-time').textContent = `${avgResponseTime.toFixed(1)}s`;
277
+ }
278
+
279
+ // Display phase breakdown
280
+ if (timelineData.phase_stats) {
281
+ const container = document.getElementById('phase-breakdown');
282
+ container.innerHTML = '';
283
+
284
+ Object.entries(timelineData.phase_stats).forEach(([phaseName, stats]) => {
285
+ const item = document.createElement('div');
286
+ item.className = 'phase-stat-item';
287
+ item.innerHTML = `
288
+ <div class="phase-name">${phaseName}</div>
289
+ <div class="phase-tokens">${formatNumber(stats.tokens)} tokens</div>
290
+ <div class="phase-calls">${formatNumber(stats.count)} calls</div>
291
+ `;
292
+ container.appendChild(item);
293
+ });
294
+ }
295
+ }
296
+
297
+ // Display search metrics
298
+ function displaySearchMetrics(searchData) {
299
+ if (!searchData) return;
300
+
301
+ // Update search summary metrics
302
+ const totalSearches = searchData.total_searches || 0;
303
+ const totalResults = searchData.total_results || 0;
304
+ const avgResponseTime = searchData.avg_response_time || 0;
305
+ const successRate = searchData.success_rate || 0;
306
+
307
+ document.getElementById('total-searches').textContent = formatNumber(totalSearches);
308
+ document.getElementById('total-search-results').textContent = formatNumber(totalResults);
309
+ document.getElementById('avg-search-response-time').textContent = `${avgResponseTime.toFixed(0)}ms`;
310
+ document.getElementById('search-success-rate').textContent = `${successRate.toFixed(1)}%`;
311
+
312
+ // Display search engine breakdown
313
+ if (!searchData.search_calls) return;
314
+
315
+ const container = document.getElementById('search-engine-breakdown');
316
+ container.innerHTML = '';
317
+
318
+ searchData.search_calls.forEach(call => {
319
+ const item = document.createElement('div');
320
+ item.className = 'search-engine-item';
321
+ item.innerHTML = `
322
+ <div class="search-engine-info">
323
+ <div class="search-engine-name">${call.engine || 'Unknown'}</div>
324
+ <div class="search-engine-query">${call.query || 'No query'}</div>
325
+ </div>
326
+ <div class="search-engine-stats">
327
+ <div class="search-results">${formatNumber(call.results_count || 0)} results</div>
328
+ <div class="search-time">${((call.response_time_ms || 0) / 1000).toFixed(1)}s</div>
329
+ </div>
330
+ `;
331
+ container.appendChild(item);
332
+ });
333
+ }
334
+
335
+ // Create timeline chart
336
+ function createTimelineChart(timelineData) {
337
+ console.log('createTimelineChart called with:', timelineData);
338
+
339
+ if (!timelineData || !timelineData.timeline) {
340
+ console.log('No timeline data for chart, timelineData:', timelineData);
341
+ return;
342
+ }
343
+
344
+ if (typeof Chart === 'undefined') {
345
+ console.error('Chart.js not loaded!');
346
+ return;
347
+ }
348
+
349
+ const chartElement = document.getElementById('timeline-chart');
350
+ if (!chartElement) {
351
+ console.error('Timeline chart element not found');
352
+ return;
353
+ }
354
+
355
+ console.log('Creating timeline chart with', timelineData.timeline.length, 'data points');
356
+
357
+ try {
358
+ const ctx = chartElement.getContext('2d');
359
+ console.log('Canvas context obtained');
360
+
361
+ // Destroy existing chart
362
+ if (timelineChart) {
363
+ timelineChart.destroy();
364
+ }
365
+
366
+ // Prepare chart data with enhanced information
367
+ const chartData = timelineData.timeline.map((item, index) => ({
368
+ phase: item.research_phase || item.phase || `Step ${index + 1}`,
369
+ tokens: item.tokens || 0,
370
+ promptTokens: item.prompt_tokens || 0,
371
+ completionTokens: item.completion_tokens || 0,
372
+ timestamp: item.timestamp || item.created_at,
373
+ responseTime: item.response_time_ms || 0
374
+ }));
375
+
376
+ const labels = chartData.map(item => item.phase);
377
+ const totalTokens = chartData.map(item => item.tokens);
378
+ const promptTokens = chartData.map(item => item.promptTokens);
379
+ const completionTokens = chartData.map(item => item.completionTokens);
380
+
381
+ console.log('Enhanced chart data:', chartData);
382
+
383
+ timelineChart = new Chart(ctx, {
384
+ type: 'bar',
385
+ data: {
386
+ labels: labels,
387
+ datasets: [
388
+ {
389
+ label: 'Input Tokens',
390
+ data: promptTokens,
391
+ backgroundColor: 'rgba(99, 102, 241, 0.8)',
392
+ borderColor: 'rgba(99, 102, 241, 1)',
393
+ borderWidth: 1,
394
+ borderRadius: 4,
395
+ borderSkipped: false,
396
+ },
397
+ {
398
+ label: 'Output Tokens',
399
+ data: completionTokens,
400
+ backgroundColor: 'rgba(34, 197, 94, 0.8)',
401
+ borderColor: 'rgba(34, 197, 94, 1)',
402
+ borderWidth: 1,
403
+ borderRadius: 4,
404
+ borderSkipped: false,
405
+ }
406
+ ]
407
+ },
408
+ options: {
409
+ responsive: true,
410
+ maintainAspectRatio: false,
411
+ animation: {
412
+ duration: 1000,
413
+ easing: 'easeInOutQuart'
414
+ },
415
+ interaction: {
416
+ intersect: false,
417
+ mode: 'index'
418
+ },
419
+ scales: {
420
+ x: {
421
+ stacked: true,
422
+ grid: {
423
+ display: false
424
+ },
425
+ ticks: {
426
+ font: {
427
+ size: 11
428
+ },
429
+ maxRotation: 45,
430
+ minRotation: 0
431
+ }
432
+ },
433
+ y: {
434
+ stacked: true,
435
+ beginAtZero: true,
436
+ grid: {
437
+ color: 'rgba(0, 0, 0, 0.1)',
438
+ drawBorder: false
439
+ },
440
+ ticks: {
441
+ font: {
442
+ size: 11
443
+ },
444
+ callback: function(value) {
445
+ return formatNumber(value);
446
+ }
447
+ },
448
+ title: {
449
+ display: true,
450
+ text: 'Tokens',
451
+ font: {
452
+ size: 12,
453
+ weight: 'bold'
454
+ }
455
+ }
456
+ }
457
+ },
458
+ plugins: {
459
+ legend: {
460
+ display: true,
461
+ position: 'top',
462
+ align: 'end',
463
+ labels: {
464
+ usePointStyle: true,
465
+ pointStyle: 'rect',
466
+ font: {
467
+ size: 11
468
+ },
469
+ padding: 15
470
+ }
471
+ },
472
+ tooltip: {
473
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
474
+ titleColor: 'white',
475
+ bodyColor: 'white',
476
+ borderColor: 'rgba(255, 255, 255, 0.1)',
477
+ borderWidth: 1,
478
+ cornerRadius: 8,
479
+ displayColors: true,
480
+ callbacks: {
481
+ title: function(tooltipItems) {
482
+ const index = tooltipItems[0].dataIndex;
483
+ const phase = chartData[index].phase;
484
+ return phase;
485
+ },
486
+ beforeBody: function(tooltipItems) {
487
+ const index = tooltipItems[0].dataIndex;
488
+ const data = chartData[index];
489
+ const total = data.tokens;
490
+ return [`Total: ${formatNumber(total)} tokens`];
491
+ },
492
+ afterBody: function(tooltipItems) {
493
+ const index = tooltipItems[0].dataIndex;
494
+ const data = chartData[index];
495
+ const lines = [];
496
+
497
+ if (data.responseTime > 0) {
498
+ lines.push(`Response time: ${(data.responseTime / 1000).toFixed(1)}s`);
499
+ }
500
+
501
+ if (data.timestamp) {
502
+ const time = new Date(data.timestamp).toLocaleTimeString();
503
+ lines.push(`Time: ${time}`);
504
+ }
505
+
506
+ return lines;
507
+ }
508
+ }
509
+ }
510
+ }
511
+ }
512
+ });
513
+
514
+ console.log('Timeline chart created successfully');
515
+ } catch (error) {
516
+ console.error('Error creating timeline chart:', error);
517
+ console.error('Chart error details:', error.message, error.stack);
518
+ }
519
+ }
520
+
521
+ // Create search chart
522
+ function createSearchChart(searchData) {
523
+ if (!searchData || !searchData.search_calls) {
524
+ console.log('No search data for chart');
525
+ return;
526
+ }
527
+
528
+ const chartElement = document.getElementById('search-chart');
529
+ if (!chartElement) {
530
+ console.error('Search chart element not found');
531
+ return;
532
+ }
533
+
534
+ const ctx = chartElement.getContext('2d');
535
+
536
+ // Destroy existing chart
537
+ if (searchChart) {
538
+ searchChart.destroy();
539
+ }
540
+
541
+ // Prepare enhanced search data
542
+ const searchCalls = searchData.search_calls.map((call, index) => ({
543
+ label: call.query ? call.query.substring(0, 20) + '...' : `Search ${index + 1}`,
544
+ results: call.results_count || 0,
545
+ engine: call.engine || 'Unknown',
546
+ responseTime: call.response_time_ms || 0,
547
+ timestamp: call.timestamp
548
+ }));
549
+
550
+ const labels = searchCalls.map(call => call.label);
551
+ const results = searchCalls.map(call => call.results);
552
+
553
+ searchChart = new Chart(ctx, {
554
+ type: 'line',
555
+ data: {
556
+ labels: labels,
557
+ datasets: [{
558
+ label: 'Results Found',
559
+ data: results,
560
+ borderColor: 'rgba(168, 85, 247, 1)',
561
+ backgroundColor: 'rgba(168, 85, 247, 0.1)',
562
+ borderWidth: 2.5,
563
+ fill: true,
564
+ tension: 0.3,
565
+ pointBackgroundColor: 'rgba(168, 85, 247, 1)',
566
+ pointBorderColor: 'rgba(255, 255, 255, 1)',
567
+ pointBorderWidth: 2,
568
+ pointRadius: 4,
569
+ pointHoverRadius: 6
570
+ }]
571
+ },
572
+ options: {
573
+ responsive: true,
574
+ maintainAspectRatio: false,
575
+ animation: {
576
+ duration: 800,
577
+ easing: 'easeInOutQuart'
578
+ },
579
+ interaction: {
580
+ intersect: false,
581
+ mode: 'index'
582
+ },
583
+ scales: {
584
+ x: {
585
+ grid: {
586
+ display: false
587
+ },
588
+ ticks: {
589
+ font: {
590
+ size: 10
591
+ },
592
+ maxRotation: 45
593
+ }
594
+ },
595
+ y: {
596
+ beginAtZero: true,
597
+ grid: {
598
+ color: 'rgba(0, 0, 0, 0.1)',
599
+ drawBorder: false
600
+ },
601
+ ticks: {
602
+ font: {
603
+ size: 10
604
+ },
605
+ callback: function(value) {
606
+ return formatNumber(value);
607
+ }
608
+ },
609
+ title: {
610
+ display: true,
611
+ text: 'Results',
612
+ font: {
613
+ size: 11,
614
+ weight: 'bold'
615
+ }
616
+ }
617
+ }
618
+ },
619
+ plugins: {
620
+ legend: {
621
+ display: false
622
+ },
623
+ tooltip: {
624
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
625
+ titleColor: 'white',
626
+ bodyColor: 'white',
627
+ borderColor: 'rgba(255, 255, 255, 0.1)',
628
+ borderWidth: 1,
629
+ cornerRadius: 6,
630
+ callbacks: {
631
+ title: function(tooltipItems) {
632
+ const index = tooltipItems[0].dataIndex;
633
+ return searchCalls[index].label;
634
+ },
635
+ beforeBody: function(tooltipItems) {
636
+ const index = tooltipItems[0].dataIndex;
637
+ const call = searchCalls[index];
638
+ return [`Engine: ${call.engine}`];
639
+ },
640
+ afterBody: function(tooltipItems) {
641
+ const index = tooltipItems[0].dataIndex;
642
+ const call = searchCalls[index];
643
+ const lines = [];
644
+
645
+ if (call.responseTime > 0) {
646
+ lines.push(`Response time: ${(call.responseTime / 1000).toFixed(1)}s`);
647
+ }
648
+
649
+ return lines;
650
+ }
651
+ }
652
+ }
653
+ }
654
+ }
655
+ });
656
+ }
657
+
658
+ // Load cost data
659
+ async function loadCostData() {
660
+ try {
661
+ // Temporarily disable cost calculation until pricing logic is optimized
662
+ document.getElementById('total-cost').textContent = '-';
663
+ return;
664
+
665
+ const response = await fetch(`/metrics/api/research-costs/${researchId}`);
666
+ if (response.ok) {
667
+ const data = await response.json();
668
+ if (data.status === 'success') {
669
+ document.getElementById('total-cost').textContent = formatCurrency(data.total_cost || 0);
670
+ }
671
+ }
672
+ } catch (error) {
673
+ console.error('Error loading cost data:', error);
674
+ document.getElementById('total-cost').textContent = '-';
675
+ }
676
+ }
677
+
678
+ // Show error message
679
+ function showError() {
680
+ document.getElementById('loading').style.display = 'none';
681
+ document.getElementById('error').style.display = 'block';
682
+ document.getElementById('details-content').style.display = 'none';
683
+ }
684
+
685
+ // Check if all required DOM elements exist
686
+ function checkRequiredElements() {
687
+ const requiredIds = [
688
+ 'loading', 'error', 'details-content', 'total-tokens', 'prompt-tokens',
689
+ 'completion-tokens', 'llm-calls', 'avg-response-time', 'model-used',
690
+ 'research-query', 'research-mode', 'research-date', 'research-strategy', 'total-cost',
691
+ 'phase-breakdown', 'search-engine-breakdown', 'timeline-chart', 'search-chart'
692
+ ];
693
+
694
+ const missing = [];
695
+ requiredIds.forEach(id => {
696
+ if (!document.getElementById(id)) {
697
+ missing.push(id);
698
+ }
699
+ });
700
+
701
+ if (missing.length > 0) {
702
+ console.error('Missing required DOM elements:', missing);
703
+ return false;
704
+ }
705
+ return true;
706
+ }
707
+
708
+ // Initialize when DOM is ready
709
+ document.addEventListener('DOMContentLoaded', function() {
710
+ console.log('DOM loaded, initializing details page');
711
+
712
+ researchId = getResearchIdFromUrl();
713
+ console.log('Research ID from URL:', researchId);
714
+
715
+ if (!researchId) {
716
+ console.error('No research ID found in URL');
717
+ showError();
718
+ return;
719
+ }
720
+
721
+ // Check if all required elements exist
722
+ if (!checkRequiredElements()) {
723
+ console.error('Required DOM elements missing');
724
+ showError();
725
+ return;
726
+ }
727
+
728
+ // Update page title
729
+ document.title = `Research Details #${researchId} - Deep Research System`;
730
+
731
+ // Load research metrics
732
+ loadResearchMetrics();
733
+
734
+ // View Results button
735
+ const viewResultsBtn = document.getElementById('view-results-btn');
736
+ if (viewResultsBtn) {
737
+ viewResultsBtn.addEventListener('click', () => {
738
+ window.location.href = `/research/results/${researchId}`;
739
+ });
740
+ }
741
+
742
+ // Back button
743
+ const backBtn = document.getElementById('back-to-history');
744
+ if (backBtn) {
745
+ backBtn.addEventListener('click', () => {
746
+ window.location.href = '/research/history';
747
+ });
748
+ }
749
+ });
750
+
751
+ })();