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.
- local_deep_research/__init__.py +23 -22
- local_deep_research/__main__.py +16 -0
- local_deep_research/advanced_search_system/__init__.py +7 -0
- local_deep_research/advanced_search_system/filters/__init__.py +8 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +38 -0
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +200 -0
- local_deep_research/advanced_search_system/findings/base_findings.py +81 -0
- local_deep_research/advanced_search_system/findings/repository.py +452 -0
- local_deep_research/advanced_search_system/knowledge/__init__.py +1 -0
- local_deep_research/advanced_search_system/knowledge/base_knowledge.py +151 -0
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +159 -0
- local_deep_research/advanced_search_system/questions/__init__.py +1 -0
- local_deep_research/advanced_search_system/questions/base_question.py +64 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +445 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +119 -0
- local_deep_research/advanced_search_system/repositories/__init__.py +7 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +1 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +118 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +450 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +312 -0
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +270 -0
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +300 -0
- local_deep_research/advanced_search_system/tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/base_tool.py +100 -0
- local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/question_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/search_tools/__init__.py +1 -0
- local_deep_research/api/__init__.py +5 -5
- local_deep_research/api/research_functions.py +154 -160
- local_deep_research/app.py +8 -0
- local_deep_research/citation_handler.py +25 -16
- local_deep_research/{config.py → config/config_files.py} +102 -110
- local_deep_research/config/llm_config.py +472 -0
- local_deep_research/config/search_config.py +77 -0
- local_deep_research/defaults/__init__.py +10 -5
- local_deep_research/defaults/main.toml +2 -2
- local_deep_research/defaults/search_engines.toml +60 -34
- local_deep_research/main.py +121 -19
- local_deep_research/migrate_db.py +147 -0
- local_deep_research/report_generator.py +87 -45
- local_deep_research/search_system.py +153 -283
- local_deep_research/setup_data_dir.py +35 -0
- local_deep_research/test_migration.py +178 -0
- local_deep_research/utilities/__init__.py +0 -0
- local_deep_research/utilities/db_utils.py +49 -0
- local_deep_research/{utilties → utilities}/enums.py +2 -2
- local_deep_research/{utilties → utilities}/llm_utils.py +63 -29
- local_deep_research/utilities/search_utilities.py +242 -0
- local_deep_research/{utilties → utilities}/setup_utils.py +4 -2
- local_deep_research/web/__init__.py +0 -1
- local_deep_research/web/app.py +86 -1709
- local_deep_research/web/app_factory.py +289 -0
- local_deep_research/web/database/README.md +70 -0
- local_deep_research/web/database/migrate_to_ldr_db.py +289 -0
- local_deep_research/web/database/migrations.py +447 -0
- local_deep_research/web/database/models.py +117 -0
- local_deep_research/web/database/schema_upgrade.py +107 -0
- local_deep_research/web/models/database.py +294 -0
- local_deep_research/web/models/settings.py +94 -0
- local_deep_research/web/routes/api_routes.py +559 -0
- local_deep_research/web/routes/history_routes.py +354 -0
- local_deep_research/web/routes/research_routes.py +715 -0
- local_deep_research/web/routes/settings_routes.py +1583 -0
- local_deep_research/web/services/research_service.py +947 -0
- local_deep_research/web/services/resource_service.py +149 -0
- local_deep_research/web/services/settings_manager.py +669 -0
- local_deep_research/web/services/settings_service.py +187 -0
- local_deep_research/web/services/socket_service.py +210 -0
- local_deep_research/web/static/css/custom_dropdown.css +277 -0
- local_deep_research/web/static/css/settings.css +1223 -0
- local_deep_research/web/static/css/styles.css +525 -48
- local_deep_research/web/static/js/components/custom_dropdown.js +428 -0
- local_deep_research/web/static/js/components/detail.js +348 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +122 -0
- local_deep_research/web/static/js/components/fallback/ui.js +215 -0
- local_deep_research/web/static/js/components/history.js +487 -0
- local_deep_research/web/static/js/components/logpanel.js +949 -0
- local_deep_research/web/static/js/components/progress.js +1107 -0
- local_deep_research/web/static/js/components/research.js +1865 -0
- local_deep_research/web/static/js/components/results.js +766 -0
- local_deep_research/web/static/js/components/settings.js +3981 -0
- local_deep_research/web/static/js/components/settings_sync.js +106 -0
- local_deep_research/web/static/js/main.js +226 -0
- local_deep_research/web/static/js/services/api.js +253 -0
- local_deep_research/web/static/js/services/audio.js +31 -0
- local_deep_research/web/static/js/services/formatting.js +119 -0
- local_deep_research/web/static/js/services/pdf.js +622 -0
- local_deep_research/web/static/js/services/socket.js +882 -0
- local_deep_research/web/static/js/services/ui.js +546 -0
- local_deep_research/web/templates/base.html +72 -0
- local_deep_research/web/templates/components/custom_dropdown.html +47 -0
- local_deep_research/web/templates/components/log_panel.html +32 -0
- local_deep_research/web/templates/components/mobile_nav.html +22 -0
- local_deep_research/web/templates/components/settings_form.html +299 -0
- local_deep_research/web/templates/components/sidebar.html +21 -0
- local_deep_research/web/templates/pages/details.html +73 -0
- local_deep_research/web/templates/pages/history.html +51 -0
- local_deep_research/web/templates/pages/progress.html +57 -0
- local_deep_research/web/templates/pages/research.html +139 -0
- local_deep_research/web/templates/pages/results.html +59 -0
- local_deep_research/web/templates/settings_dashboard.html +78 -192
- local_deep_research/web/utils/__init__.py +0 -0
- local_deep_research/web/utils/formatters.py +76 -0
- local_deep_research/web_search_engines/engines/full_search.py +18 -16
- local_deep_research/web_search_engines/engines/meta_search_engine.py +182 -131
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +224 -139
- local_deep_research/web_search_engines/engines/search_engine_brave.py +88 -71
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +48 -39
- local_deep_research/web_search_engines/engines/search_engine_github.py +415 -204
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +123 -90
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +210 -157
- local_deep_research/web_search_engines/engines/search_engine_local.py +532 -369
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +42 -36
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +358 -266
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +212 -160
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +213 -170
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +84 -68
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +186 -154
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +115 -77
- local_deep_research/web_search_engines/search_engine_base.py +174 -99
- local_deep_research/web_search_engines/search_engine_factory.py +192 -102
- local_deep_research/web_search_engines/search_engines_config.py +22 -15
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/METADATA +177 -97
- local_deep_research-0.2.2.dist-info/RECORD +135 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/WHEEL +1 -2
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/entry_points.txt +3 -0
- local_deep_research/defaults/llm_config.py +0 -338
- local_deep_research/utilties/search_utilities.py +0 -114
- local_deep_research/web/static/js/app.js +0 -3763
- local_deep_research/web/templates/api_keys_config.html +0 -82
- local_deep_research/web/templates/collections_config.html +0 -90
- local_deep_research/web/templates/index.html +0 -348
- local_deep_research/web/templates/llm_config.html +0 -120
- local_deep_research/web/templates/main_config.html +0 -89
- local_deep_research/web/templates/search_engines_config.html +0 -154
- local_deep_research/web/templates/settings.html +0 -519
- local_deep_research-0.1.26.dist-info/RECORD +0 -61
- local_deep_research-0.1.26.dist-info/top_level.txt +0 -1
- /local_deep_research/{utilties → config}/__init__.py +0 -0
- {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');
|