local-deep-research 0.1.26__py3-none-any.whl → 0.2.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 (140) hide show
  1. local_deep_research/__init__.py +23 -22
  2. local_deep_research/__main__.py +16 -0
  3. local_deep_research/advanced_search_system/__init__.py +7 -0
  4. local_deep_research/advanced_search_system/filters/__init__.py +8 -0
  5. local_deep_research/advanced_search_system/filters/base_filter.py +38 -0
  6. local_deep_research/advanced_search_system/filters/cross_engine_filter.py +200 -0
  7. local_deep_research/advanced_search_system/findings/base_findings.py +81 -0
  8. local_deep_research/advanced_search_system/findings/repository.py +452 -0
  9. local_deep_research/advanced_search_system/knowledge/__init__.py +1 -0
  10. local_deep_research/advanced_search_system/knowledge/base_knowledge.py +151 -0
  11. local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +159 -0
  12. local_deep_research/advanced_search_system/questions/__init__.py +1 -0
  13. local_deep_research/advanced_search_system/questions/base_question.py +64 -0
  14. local_deep_research/advanced_search_system/questions/decomposition_question.py +445 -0
  15. local_deep_research/advanced_search_system/questions/standard_question.py +119 -0
  16. local_deep_research/advanced_search_system/repositories/__init__.py +7 -0
  17. local_deep_research/advanced_search_system/strategies/__init__.py +1 -0
  18. local_deep_research/advanced_search_system/strategies/base_strategy.py +118 -0
  19. local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +450 -0
  20. local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +312 -0
  21. local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +270 -0
  22. local_deep_research/advanced_search_system/strategies/standard_strategy.py +300 -0
  23. local_deep_research/advanced_search_system/tools/__init__.py +1 -0
  24. local_deep_research/advanced_search_system/tools/base_tool.py +100 -0
  25. local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +1 -0
  26. local_deep_research/advanced_search_system/tools/question_tools/__init__.py +1 -0
  27. local_deep_research/advanced_search_system/tools/search_tools/__init__.py +1 -0
  28. local_deep_research/api/__init__.py +5 -5
  29. local_deep_research/api/research_functions.py +154 -160
  30. local_deep_research/app.py +8 -0
  31. local_deep_research/citation_handler.py +25 -16
  32. local_deep_research/{config.py → config/config_files.py} +102 -110
  33. local_deep_research/config/llm_config.py +472 -0
  34. local_deep_research/config/search_config.py +77 -0
  35. local_deep_research/defaults/__init__.py +10 -5
  36. local_deep_research/defaults/main.toml +2 -2
  37. local_deep_research/defaults/search_engines.toml +60 -34
  38. local_deep_research/main.py +121 -19
  39. local_deep_research/migrate_db.py +147 -0
  40. local_deep_research/report_generator.py +87 -45
  41. local_deep_research/search_system.py +153 -283
  42. local_deep_research/setup_data_dir.py +35 -0
  43. local_deep_research/test_migration.py +178 -0
  44. local_deep_research/utilities/__init__.py +0 -0
  45. local_deep_research/utilities/db_utils.py +49 -0
  46. local_deep_research/{utilties → utilities}/enums.py +2 -2
  47. local_deep_research/{utilties → utilities}/llm_utils.py +63 -29
  48. local_deep_research/utilities/search_utilities.py +242 -0
  49. local_deep_research/{utilties → utilities}/setup_utils.py +4 -2
  50. local_deep_research/web/__init__.py +0 -1
  51. local_deep_research/web/app.py +86 -1709
  52. local_deep_research/web/app_factory.py +289 -0
  53. local_deep_research/web/database/README.md +70 -0
  54. local_deep_research/web/database/migrate_to_ldr_db.py +289 -0
  55. local_deep_research/web/database/migrations.py +447 -0
  56. local_deep_research/web/database/models.py +117 -0
  57. local_deep_research/web/database/schema_upgrade.py +107 -0
  58. local_deep_research/web/models/database.py +294 -0
  59. local_deep_research/web/models/settings.py +94 -0
  60. local_deep_research/web/routes/api_routes.py +559 -0
  61. local_deep_research/web/routes/history_routes.py +354 -0
  62. local_deep_research/web/routes/research_routes.py +715 -0
  63. local_deep_research/web/routes/settings_routes.py +1583 -0
  64. local_deep_research/web/services/research_service.py +947 -0
  65. local_deep_research/web/services/resource_service.py +149 -0
  66. local_deep_research/web/services/settings_manager.py +669 -0
  67. local_deep_research/web/services/settings_service.py +187 -0
  68. local_deep_research/web/services/socket_service.py +210 -0
  69. local_deep_research/web/static/css/custom_dropdown.css +277 -0
  70. local_deep_research/web/static/css/settings.css +1223 -0
  71. local_deep_research/web/static/css/styles.css +525 -48
  72. local_deep_research/web/static/js/components/custom_dropdown.js +428 -0
  73. local_deep_research/web/static/js/components/detail.js +348 -0
  74. local_deep_research/web/static/js/components/fallback/formatting.js +122 -0
  75. local_deep_research/web/static/js/components/fallback/ui.js +215 -0
  76. local_deep_research/web/static/js/components/history.js +487 -0
  77. local_deep_research/web/static/js/components/logpanel.js +949 -0
  78. local_deep_research/web/static/js/components/progress.js +1107 -0
  79. local_deep_research/web/static/js/components/research.js +1865 -0
  80. local_deep_research/web/static/js/components/results.js +766 -0
  81. local_deep_research/web/static/js/components/settings.js +3981 -0
  82. local_deep_research/web/static/js/components/settings_sync.js +106 -0
  83. local_deep_research/web/static/js/main.js +226 -0
  84. local_deep_research/web/static/js/services/api.js +253 -0
  85. local_deep_research/web/static/js/services/audio.js +31 -0
  86. local_deep_research/web/static/js/services/formatting.js +119 -0
  87. local_deep_research/web/static/js/services/pdf.js +622 -0
  88. local_deep_research/web/static/js/services/socket.js +882 -0
  89. local_deep_research/web/static/js/services/ui.js +546 -0
  90. local_deep_research/web/templates/base.html +72 -0
  91. local_deep_research/web/templates/components/custom_dropdown.html +47 -0
  92. local_deep_research/web/templates/components/log_panel.html +32 -0
  93. local_deep_research/web/templates/components/mobile_nav.html +22 -0
  94. local_deep_research/web/templates/components/settings_form.html +299 -0
  95. local_deep_research/web/templates/components/sidebar.html +21 -0
  96. local_deep_research/web/templates/pages/details.html +73 -0
  97. local_deep_research/web/templates/pages/history.html +51 -0
  98. local_deep_research/web/templates/pages/progress.html +57 -0
  99. local_deep_research/web/templates/pages/research.html +139 -0
  100. local_deep_research/web/templates/pages/results.html +59 -0
  101. local_deep_research/web/templates/settings_dashboard.html +78 -192
  102. local_deep_research/web/utils/__init__.py +0 -0
  103. local_deep_research/web/utils/formatters.py +76 -0
  104. local_deep_research/web_search_engines/engines/full_search.py +18 -16
  105. local_deep_research/web_search_engines/engines/meta_search_engine.py +182 -131
  106. local_deep_research/web_search_engines/engines/search_engine_arxiv.py +224 -139
  107. local_deep_research/web_search_engines/engines/search_engine_brave.py +88 -71
  108. local_deep_research/web_search_engines/engines/search_engine_ddg.py +48 -39
  109. local_deep_research/web_search_engines/engines/search_engine_github.py +415 -204
  110. local_deep_research/web_search_engines/engines/search_engine_google_pse.py +123 -90
  111. local_deep_research/web_search_engines/engines/search_engine_guardian.py +210 -157
  112. local_deep_research/web_search_engines/engines/search_engine_local.py +532 -369
  113. local_deep_research/web_search_engines/engines/search_engine_local_all.py +42 -36
  114. local_deep_research/web_search_engines/engines/search_engine_pubmed.py +358 -266
  115. local_deep_research/web_search_engines/engines/search_engine_searxng.py +212 -160
  116. local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +213 -170
  117. local_deep_research/web_search_engines/engines/search_engine_serpapi.py +84 -68
  118. local_deep_research/web_search_engines/engines/search_engine_wayback.py +186 -154
  119. local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +115 -77
  120. local_deep_research/web_search_engines/search_engine_base.py +174 -99
  121. local_deep_research/web_search_engines/search_engine_factory.py +192 -102
  122. local_deep_research/web_search_engines/search_engines_config.py +22 -15
  123. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/METADATA +177 -97
  124. local_deep_research-0.2.2.dist-info/RECORD +135 -0
  125. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/WHEEL +1 -2
  126. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/entry_points.txt +3 -0
  127. local_deep_research/defaults/llm_config.py +0 -338
  128. local_deep_research/utilties/search_utilities.py +0 -114
  129. local_deep_research/web/static/js/app.js +0 -3763
  130. local_deep_research/web/templates/api_keys_config.html +0 -82
  131. local_deep_research/web/templates/collections_config.html +0 -90
  132. local_deep_research/web/templates/index.html +0 -348
  133. local_deep_research/web/templates/llm_config.html +0 -120
  134. local_deep_research/web/templates/main_config.html +0 -89
  135. local_deep_research/web/templates/search_engines_config.html +0 -154
  136. local_deep_research/web/templates/settings.html +0 -519
  137. local_deep_research-0.1.26.dist-info/RECORD +0 -61
  138. local_deep_research-0.1.26.dist-info/top_level.txt +0 -1
  139. /local_deep_research/{utilties → config}/__init__.py +0 -0
  140. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,106 @@
1
+ // Function to save settings using the settings page endpoint
2
+ function saveMenuSettings(settingKey, settingValue) {
3
+ // Create payload with the single setting being changed
4
+ const payload = {};
5
+ payload[settingKey] = settingValue;
6
+
7
+ console.log('Saving setting:', settingKey, '=', settingValue);
8
+
9
+ // Get CSRF token
10
+ const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
11
+
12
+ // Use fetch to send to the same endpoint as the settings page
13
+ fetch('/research/settings/save_all_settings', {
14
+ method: 'POST',
15
+ headers: {
16
+ 'Content-Type': 'application/json',
17
+ 'X-CSRFToken': csrfToken
18
+ },
19
+ body: JSON.stringify(payload)
20
+ })
21
+ .then(response => response.json())
22
+ .then(data => {
23
+ if (data.status === 'success') {
24
+ // Show success notification if UI module is available
25
+ if (window.ui && window.ui.showMessage) {
26
+ window.ui.showMessage('Setting saved successfully', 'success');
27
+ } else {
28
+ console.log('Setting saved successfully:', data);
29
+ }
30
+ } else {
31
+ // Show error notification
32
+ if (window.ui && window.ui.showMessage) {
33
+ window.ui.showMessage('Failed to save setting: ' + (data.message || 'Unknown error'), 'error');
34
+ } else {
35
+ console.error('Failed to save setting:', data);
36
+ }
37
+ }
38
+ })
39
+ .catch(error => {
40
+ console.error('Error saving setting:', error);
41
+ if (window.ui && window.ui.showMessage) {
42
+ window.ui.showMessage('Error saving setting', 'error');
43
+ }
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Connects the menu settings to use the same save method as the settings page.
49
+ */
50
+ function connectMenuSettings() {
51
+ console.log('Initializing menu settings handler');
52
+
53
+ // Handle model dropdown changes
54
+ const modelInput = document.getElementById('model');
55
+ const modelHidden = document.getElementById('model_hidden');
56
+
57
+ if (modelHidden) {
58
+ modelHidden.addEventListener('change', function(e) {
59
+ console.log('Model changed to:', this.value);
60
+ saveMenuSettings('llm.model', this.value);
61
+ });
62
+ }
63
+
64
+ // Handle provider dropdown changes
65
+ const providerSelect = document.getElementById('model_provider');
66
+ if (providerSelect) {
67
+ providerSelect.addEventListener('change', function(e) {
68
+ console.log('Provider changed to:', this.value);
69
+ saveMenuSettings('llm.provider', this.value);
70
+ });
71
+ }
72
+
73
+ // Handle search engine dropdown changes
74
+ const searchEngineHidden = document.getElementById('search_engine_hidden');
75
+ if (searchEngineHidden) {
76
+ searchEngineHidden.addEventListener('change', function(e) {
77
+ console.log('Search engine changed to:', this.value);
78
+ saveMenuSettings('search.tool', this.value);
79
+ });
80
+ }
81
+
82
+ // Handle iterations and questions per iteration
83
+ const iterationsInput = document.getElementById('iterations');
84
+ if (iterationsInput) {
85
+ iterationsInput.addEventListener('change', function(e) {
86
+ console.log('Iterations changed to:', this.value);
87
+ saveMenuSettings('search.iterations', this.value);
88
+ });
89
+ }
90
+
91
+ const questionsInput = document.getElementById('questions_per_iteration');
92
+ if (questionsInput) {
93
+ questionsInput.addEventListener('change', function(e) {
94
+ console.log('Questions per iteration changed to:', this.value);
95
+ saveMenuSettings('search.questions_per_iteration', this.value);
96
+ });
97
+ }
98
+
99
+ console.log('Menu settings handlers initialized');
100
+ }
101
+
102
+ // Call this function after the page and other scripts are loaded
103
+ document.addEventListener('DOMContentLoaded', function() {
104
+ // Wait a bit to ensure other scripts have loaded
105
+ setTimeout(connectMenuSettings, 1000);
106
+ });
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Main JavaScript Entry Point
3
+ * Loads appropriate components based on the current page
4
+ */
5
+ (function() {
6
+ // Map of page identifiers to component scripts
7
+ const pageComponents = {
8
+ 'page-home': ['research.js'],
9
+ 'page-progress': ['progress.js'],
10
+ 'page-results': ['results.js'],
11
+ 'page-detail': ['detail.js'],
12
+ 'page-history': ['history.js'],
13
+ 'page-settings': ['settings.js']
14
+ };
15
+
16
+ // Core services to always load
17
+ const coreServices = [
18
+ 'formatting.js',
19
+ 'ui.js',
20
+ 'api.js',
21
+ 'socket.js'
22
+ // 'audio.js' - Removed from here, loaded separately
23
+ ];
24
+
25
+ // Optional services to load when needed
26
+ const optionalServices = {
27
+ 'page-results': ['pdf.js'],
28
+ 'page-detail': ['pdf.js']
29
+ };
30
+
31
+ /**
32
+ * Initialize the application
33
+ */
34
+ function initializeApp() {
35
+ // Detect current page
36
+ const currentPage = detectCurrentPage();
37
+
38
+ if (!currentPage) {
39
+ console.error('Cannot detect current page type');
40
+ return;
41
+ }
42
+
43
+ console.log('Current page detected:', currentPage);
44
+
45
+ // IMPORTANT: Load audio.js first, before ANY other scripts
46
+ loadAudioServiceFirst(() => {
47
+ // Continue loading other scripts after audio service is loaded
48
+ console.log('Audio service script loaded, continuing with other scripts');
49
+
50
+ // Load UI and formatting utils
51
+ loadScripts('utils', coreServices.filter(s => s.includes('formatting') || s.includes('ui')));
52
+
53
+ // Then load the rest
54
+ loadScripts('services', coreServices.filter(s => s.includes('api') || s.includes('socket')));
55
+
56
+ // Load optional services for this page
57
+ if (optionalServices[currentPage]) {
58
+ loadScripts('services', optionalServices[currentPage]);
59
+ }
60
+
61
+ // Load components for this page AFTER all services
62
+ if (pageComponents[currentPage]) {
63
+ loadScripts('components', pageComponents[currentPage]);
64
+ }
65
+
66
+ // Initialize tooltips and other global UI elements
67
+ initializeGlobalUI();
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Load audio service first and separately to ensure it's fully loaded before other scripts
73
+ * @param {Function} callback - Function to call after audio service is loaded
74
+ */
75
+ function loadAudioServiceFirst(callback) {
76
+ console.log('Loading audio service script first...');
77
+ const audioScript = document.createElement('script');
78
+ audioScript.src = `/research/static/js/services/audio.js?t=${new Date().getTime()}`; // Add timestamp to avoid cache
79
+ audioScript.async = false;
80
+
81
+ // Set up callback for when script loads
82
+ audioScript.onload = function() {
83
+ console.log('Audio service script loaded successfully');
84
+
85
+ // Check if audio service is available in window object
86
+ setTimeout(() => {
87
+ if (window.audio) {
88
+ console.log('Audio service initialized in window object');
89
+ } else {
90
+ console.warn('Audio service not available in window object after script load');
91
+ }
92
+
93
+ // Continue regardless
94
+ callback();
95
+ }, 100); // Small delay to ensure script executes
96
+ };
97
+
98
+ // Error handling
99
+ audioScript.onerror = function() {
100
+ console.error('Failed to load audio service script');
101
+ // Continue with other scripts even if audio fails
102
+ callback();
103
+ };
104
+
105
+ // Add to document
106
+ document.body.appendChild(audioScript);
107
+ }
108
+
109
+ /**
110
+ * Detect the current page based on body class
111
+ * @returns {string|null} The page identifier or null if not found
112
+ */
113
+ function detectCurrentPage() {
114
+ const bodyClasses = document.body.classList;
115
+
116
+ for (const pageId in pageComponents) {
117
+ if (bodyClasses.contains(pageId)) {
118
+ return pageId;
119
+ }
120
+ }
121
+
122
+ // Check URL patterns as fallback
123
+ const path = window.location.pathname;
124
+
125
+ if (path === '/' || path === '/index' || path === '/home' || path === '/research/') {
126
+ return 'page-home';
127
+ } else if (path.includes('/research/progress')) {
128
+ return 'page-progress';
129
+ } else if (path.includes('/research/results')) {
130
+ return 'page-results';
131
+ } else if (path.includes('/research/detail')) {
132
+ return 'page-detail';
133
+ } else if (path.includes('/research/history')) {
134
+ return 'page-history';
135
+ } else if (path.includes('/research/settings')) {
136
+ return 'page-settings';
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * Load scripts dynamically
144
+ * @param {string} folder - The folder containing the scripts
145
+ * @param {Array} scripts - Array of script filenames to load
146
+ */
147
+ function loadScripts(folder, scripts) {
148
+ if (!scripts || !scripts.length) return;
149
+
150
+ scripts.forEach(script => {
151
+ const scriptElement = document.createElement('script');
152
+ scriptElement.src = `/research/static/js/${folder}/${script}`;
153
+ scriptElement.async = false; // Load in sequence
154
+ document.body.appendChild(scriptElement);
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Initialize global UI elements
160
+ */
161
+ function initializeGlobalUI() {
162
+ // Initialize notifications if browser supports it
163
+ if ("Notification" in window && Notification.permission === "default") {
164
+ // Only ask for permission when user interacts with the page
165
+ document.addEventListener('click', requestNotificationPermission, { once: true });
166
+ }
167
+
168
+ // Initialize theme toggle
169
+ const themeToggle = document.getElementById('theme-toggle');
170
+ if (themeToggle) {
171
+ // Check for saved theme preference or use system preference
172
+ const savedTheme = localStorage.getItem('theme');
173
+ const systemDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
174
+ const isDarkMode = savedTheme === 'dark' || (savedTheme === null && systemDarkMode);
175
+
176
+ // Set initial theme
177
+ document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
178
+ themeToggle.innerHTML = isDarkMode ?
179
+ '<i class="fas fa-sun"></i>' :
180
+ '<i class="fas fa-moon"></i>';
181
+
182
+ // Listen for theme toggle click
183
+ themeToggle.addEventListener('click', function() {
184
+ const currentTheme = document.documentElement.getAttribute('data-theme');
185
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
186
+
187
+ document.documentElement.setAttribute('data-theme', newTheme);
188
+ localStorage.setItem('theme', newTheme);
189
+
190
+ themeToggle.innerHTML = newTheme === 'dark' ?
191
+ '<i class="fas fa-sun"></i>' :
192
+ '<i class="fas fa-moon"></i>';
193
+ });
194
+ }
195
+
196
+ // Initialize mobile menu toggle
197
+ const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
198
+ const navMenu = document.getElementById('main-nav');
199
+
200
+ if (mobileMenuToggle && navMenu) {
201
+ mobileMenuToggle.addEventListener('click', function() {
202
+ navMenu.classList.toggle('open');
203
+ mobileMenuToggle.setAttribute(
204
+ 'aria-expanded',
205
+ navMenu.classList.contains('open') ? 'true' : 'false'
206
+ );
207
+ });
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Request notification permission
213
+ */
214
+ function requestNotificationPermission() {
215
+ if ("Notification" in window && Notification.permission === "default") {
216
+ Notification.requestPermission();
217
+ }
218
+ }
219
+
220
+ // Initialize when DOM is ready
221
+ if (document.readyState === 'loading') {
222
+ document.addEventListener('DOMContentLoaded', initializeApp);
223
+ } else {
224
+ initializeApp();
225
+ }
226
+ })();
@@ -0,0 +1,253 @@
1
+ /**
2
+ * API Service
3
+ * Handles all communication with the server API endpoints
4
+ */
5
+
6
+ // Base URL for API - use existing one if already declared
7
+ if (typeof API_BASE_URL === 'undefined') {
8
+ const API_BASE_URL = '/research/api';
9
+ }
10
+
11
+ /**
12
+ * Get CSRF token from meta tag
13
+ * @returns {string} The CSRF token
14
+ */
15
+ function getCsrfToken() {
16
+ const metaTag = document.querySelector('meta[name="csrf-token"]');
17
+ return metaTag ? metaTag.getAttribute('content') : '';
18
+ }
19
+
20
+ /**
21
+ * Get the full URL for an API endpoint
22
+ * @param {string} path - The API path (without leading slash)
23
+ * @returns {string} The full API URL
24
+ */
25
+ function getApiUrl(path) {
26
+ return `${API_BASE_URL}/${path}`;
27
+ }
28
+
29
+ /**
30
+ * Generic fetch with error handling
31
+ * @param {string} url - The URL to fetch
32
+ * @param {Object} options - Fetch options
33
+ * @returns {Promise<any>} The parsed response data
34
+ */
35
+ async function fetchWithErrorHandling(url, options = {}) {
36
+ try {
37
+ const csrfToken = getCsrfToken();
38
+ const response = await fetch(url, {
39
+ ...options,
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ ...(csrfToken ? { 'X-CSRFToken': csrfToken } : {}),
43
+ ...(options.headers || {})
44
+ }
45
+ });
46
+
47
+ // Handle non-200 responses
48
+ if (!response.ok) {
49
+ const errorData = await response.json().catch(() => ({}));
50
+ throw new Error(errorData.message || `API Error: ${response.status} ${response.statusText}`);
51
+ }
52
+
53
+ // Parse the response
54
+ return await response.json();
55
+ } catch (error) {
56
+ console.error('API Error:', error);
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Helper method to perform POST requests with JSON data
63
+ * @param {string} path - API path
64
+ * @param {Object} data - JSON data to send
65
+ * @returns {Promise<any>} Response data
66
+ */
67
+ async function postJSON(path, data) {
68
+ return fetchWithErrorHandling(path, {
69
+ method: 'POST',
70
+ body: JSON.stringify(data)
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Start a new research
76
+ * @param {string} query - The research query
77
+ * @param {string} mode - The research mode (quick/detailed)
78
+ * @returns {Promise<Object>} The research response with ID
79
+ */
80
+ async function startResearch(query, mode) {
81
+ return postJSON('/research/api/start_research', { query, mode });
82
+ }
83
+
84
+ /**
85
+ * Get a research status by ID
86
+ * @param {number} researchId - The research ID
87
+ * @returns {Promise<Object>} The research status
88
+ */
89
+ async function getResearchStatus(researchId) {
90
+ return fetchWithErrorHandling(`/research/api/status/${researchId}`);
91
+ }
92
+
93
+ /**
94
+ * Get research details
95
+ * @param {number} researchId - The research ID
96
+ * @returns {Promise<Object>} The research details
97
+ */
98
+ async function getResearchDetails(researchId) {
99
+ return fetchWithErrorHandling(`/research/api/details/${researchId}`);
100
+ }
101
+
102
+ /**
103
+ * Get research logs
104
+ * @param {number} researchId - The research ID
105
+ * @returns {Promise<Object>} The research logs
106
+ */
107
+ async function getResearchLogs(researchId) {
108
+ return fetchWithErrorHandling(`/research/api/logs/${researchId}`);
109
+ }
110
+
111
+ /**
112
+ * Get research history
113
+ * @returns {Promise<Array>} The research history
114
+ */
115
+ async function getResearchHistory() {
116
+ return fetchWithErrorHandling('/research/api/history');
117
+ }
118
+
119
+ /**
120
+ * Get research report
121
+ * @param {number} researchId - The research ID
122
+ * @returns {Promise<Object>} The research report
123
+ */
124
+ async function getReport(researchId) {
125
+ return fetchWithErrorHandling(`/research/api/report/${researchId}`);
126
+ }
127
+
128
+ /**
129
+ * Terminate a research
130
+ * @param {number} researchId - The research ID
131
+ * @returns {Promise<Object>} The termination response
132
+ */
133
+ async function terminateResearch(researchId) {
134
+ return postJSON(`/research/api/terminate/${researchId}`, {});
135
+ }
136
+
137
+ /**
138
+ * Delete a research
139
+ * @param {number} researchId - The research ID
140
+ * @returns {Promise<Object>} The deletion response
141
+ */
142
+ async function deleteResearch(researchId) {
143
+ const csrfToken = getCsrfToken();
144
+ return fetchWithErrorHandling(`/research/api/delete/${researchId}`, {
145
+ method: 'DELETE',
146
+ headers: {
147
+ 'X-CSRFToken': csrfToken
148
+ }
149
+ });
150
+ }
151
+
152
+ /**
153
+ * Clear all research history
154
+ * @returns {Promise<Object>} The response
155
+ */
156
+ async function clearResearchHistory() {
157
+ return postJSON('/research/api/clear_history', {});
158
+ }
159
+
160
+ /**
161
+ * Open a file location in the system file explorer
162
+ * @param {string} path - Path to open
163
+ * @returns {Promise<Object>} The response
164
+ */
165
+ async function openFileLocation(path) {
166
+ return postJSON('/research/open_file_location', { path });
167
+ }
168
+
169
+ /**
170
+ * Save raw configuration
171
+ * @param {string} rawConfig - Raw configuration text
172
+ * @returns {Promise<Object>} The response
173
+ */
174
+ async function saveRawConfig(rawConfig) {
175
+ return postJSON('/research/api/save_raw_config', { raw_config: rawConfig });
176
+ }
177
+
178
+ /**
179
+ * Save main configuration
180
+ * @param {Object} config - Configuration object
181
+ * @returns {Promise<Object>} The response
182
+ */
183
+ async function saveMainConfig(config) {
184
+ return postJSON('/research/api/save_main_config', config);
185
+ }
186
+
187
+ /**
188
+ * Save search engines configuration
189
+ * @param {Object} config - Configuration object
190
+ * @returns {Promise<Object>} The response
191
+ */
192
+ async function saveSearchEnginesConfig(config) {
193
+ return postJSON('/research/api/save_search_engines_config', config);
194
+ }
195
+
196
+ /**
197
+ * Save collections configuration
198
+ * @param {Object} config - Configuration object
199
+ * @returns {Promise<Object>} The response
200
+ */
201
+ async function saveCollectionsConfig(config) {
202
+ return postJSON('/research/api/save_collections_config', config);
203
+ }
204
+
205
+ /**
206
+ * Save API keys configuration
207
+ * @param {Object} config - Configuration object
208
+ * @returns {Promise<Object>} The response
209
+ */
210
+ async function saveApiKeysConfig(config) {
211
+ return postJSON('/research/api/save_api_keys_config', config);
212
+ }
213
+
214
+ /**
215
+ * Save LLM configuration
216
+ * @param {Object} config - Configuration object
217
+ * @returns {Promise<Object>} The response
218
+ */
219
+ async function saveLlmConfig(config) {
220
+ return postJSON('/research/api/save_llm_config', config);
221
+ }
222
+
223
+ /**
224
+ * Get markdown export for research
225
+ * @param {number} researchId - The research ID
226
+ * @returns {Promise<Object>} The markdown content
227
+ */
228
+ async function getMarkdownExport(researchId) {
229
+ return fetchWithErrorHandling(`/research/api/markdown/${researchId}`);
230
+ }
231
+
232
+ // Export the API functions
233
+ window.api = {
234
+ startResearch,
235
+ getResearchStatus,
236
+ getResearchDetails,
237
+ getResearchLogs,
238
+ getResearchHistory,
239
+ getReport,
240
+ getMarkdownExport,
241
+ terminateResearch,
242
+ deleteResearch,
243
+ clearResearchHistory,
244
+ openFileLocation,
245
+ saveRawConfig,
246
+ saveMainConfig,
247
+ saveSearchEnginesConfig,
248
+ saveCollectionsConfig,
249
+ saveApiKeysConfig,
250
+ saveLlmConfig,
251
+ postJSON,
252
+ getApiUrl
253
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Audio Service Stub
3
+ * This is a placeholder for future sound notification implementation
4
+ */
5
+
6
+ // Set global audio object as a no-op service
7
+ window.audio = {
8
+ initialize: function() {
9
+ console.log('Audio service disabled - will be implemented in the future');
10
+ return false;
11
+ },
12
+ playSuccess: function() {
13
+ console.log('Success sound playback disabled');
14
+ return false;
15
+ },
16
+ playError: function() {
17
+ console.log('Error sound playback disabled');
18
+ return false;
19
+ },
20
+ play: function() {
21
+ console.log('Sound playback disabled');
22
+ return false;
23
+ },
24
+ test: function() {
25
+ console.log('Sound testing disabled');
26
+ return false;
27
+ }
28
+ };
29
+
30
+ // Log that audio is disabled
31
+ console.log('Audio service is currently disabled - notifications will be implemented later');