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
@@ -2,12 +2,12 @@
2
2
  # This file defines all available search engines and their properties
3
3
 
4
4
  [wikipedia]
5
- module_path = "local_deep_research.web_search_engines.engines.search_engine_wikipedia"
5
+ module_path = ".engines.search_engine_wikipedia"
6
6
  class_name = "WikipediaSearchEngine"
7
7
  requires_api_key = false
8
8
  reliability = 0.95
9
9
  strengths = [
10
- "factual information", "general knowledge", "definitions",
10
+ "factual information", "general knowledge", "definitions",
11
11
  "historical facts", "biographies", "overview information"
12
12
  ]
13
13
  weaknesses = ["recent events", "specialized academic topics", "product comparisons"]
@@ -17,12 +17,12 @@ max_results = 20
17
17
  include_content = true
18
18
 
19
19
  [arxiv]
20
- module_path = "local_deep_research.web_search_engines.engines.search_engine_arxiv"
20
+ module_path = ".engines.search_engine_arxiv"
21
21
  class_name = "ArXivSearchEngine"
22
22
  requires_api_key = false
23
23
  reliability = 0.9
24
24
  strengths = [
25
- "scientific papers", "academic research", "physics", "computer science",
25
+ "scientific papers", "academic research", "physics", "computer science",
26
26
  "mathematics", "statistics", "machine learning", "preprints"
27
27
  ]
28
28
  weaknesses = ["non-academic topics", "consumer products", "news", "general information"]
@@ -33,17 +33,17 @@ sort_by = "relevance"
33
33
  sort_order = "descending"
34
34
 
35
35
  [pubmed]
36
- module_path = "local_deep_research.web_search_engines.engines.search_engine_pubmed"
36
+ module_path = ".engines.search_engine_pubmed"
37
37
  class_name = "PubMedSearchEngine"
38
38
  requires_api_key = false
39
39
  api_key_env = "NCBI_API_KEY"
40
40
  reliability = 0.98
41
41
  strengths = [
42
- "biomedical literature", "medical research", "clinical studies",
42
+ "biomedical literature", "medical research", "clinical studies",
43
43
  "life sciences", "health information", "scientific papers"
44
44
  ]
45
45
  weaknesses = [
46
- "non-medical topics", "very recent papers may be missing",
46
+ "non-medical topics", "very recent papers may be missing",
47
47
  "limited to published research"
48
48
  ]
49
49
  requires_llm = true
@@ -57,12 +57,12 @@ days_limit = 0
57
57
  optimize_queries = true
58
58
 
59
59
  [github]
60
- module_path = "local_deep_research.web_search_engines.engines.search_engine_github"
60
+ module_path = ".engines.search_engine_github"
61
61
  class_name = "GitHubSearchEngine"
62
62
  requires_api_key = false
63
63
  reliability = 0.99
64
64
  strengths = [
65
- "code repositories", "software documentation", "open source projects",
65
+ "code repositories", "software documentation", "open source projects",
66
66
  "programming issues", "developer information", "technical documentation"
67
67
  ]
68
68
  weaknesses = ["non-technical content", "content outside GitHub", "rate limits without API key"]
@@ -75,13 +75,13 @@ include_readme = true
75
75
  include_issues = false
76
76
 
77
77
  [serpapi]
78
- module_path = "local_deep_research.web_search_engines.engines.search_engine_serpapi"
78
+ module_path = ".engines.search_engine_serpapi"
79
79
  class_name = "SerpAPISearchEngine"
80
80
  requires_api_key = true
81
81
  api_key_env = "SERP_API_KEY"
82
82
  reliability = 0.6
83
83
  strengths = [
84
- "comprehensive web search", "product information", "reviews",
84
+ "comprehensive web search", "product information", "reviews",
85
85
  "recent content", "news", "broad coverage"
86
86
  ]
87
87
  weaknesses = ["requires API key with usage limits", "not specialized for academic content"]
@@ -95,23 +95,49 @@ time_period = "y"
95
95
  safe_search = true
96
96
  search_language = "English"
97
97
 
98
+ [searxng]
99
+ module_path = ".engines.search_engine_searxng"
100
+ class_name = "SearXNGSearchEngine"
101
+ requires_api_key = false
102
+ api_key_env = "SEARXNG_INSTANCE"
103
+ reliability = 0.9
104
+ strengths = [
105
+ "privacy-focused", "metasearch engine", "self-hosted",
106
+ "no tracking", "configurable", "multiple engines in one"
107
+ ]
108
+ weaknesses = [
109
+ "requires self-hosting", "depends on other search engines",
110
+ "may be rate limited by underlying engines"
111
+ ]
112
+ supports_full_search = true
113
+ full_search_module = ".engines.full_search"
114
+ full_search_class = "FullSearchResults"
115
+
116
+ [searxng.default_params]
117
+ max_results = 15
118
+ categories = ["general"]
119
+ language = "en"
120
+ safe_search = 1
121
+ delay_between_requests = 0.0
122
+ include_full_content = true
123
+
98
124
  [google_pse]
99
- module_path = "local_deep_research.web_search_engines.engines.search_engine_google_pse"
125
+ module_path = ".engines.search_engine_google_pse"
100
126
  class_name = "GooglePSESearchEngine"
101
127
  requires_api_key = true
102
128
  api_key_env = "GOOGLE_PSE_API_KEY"
103
129
  reliability = 0.9
104
130
  strengths = [
105
- "custom search scope", "high-quality results", "domain-specific search",
131
+ "custom search scope", "high-quality results", "domain-specific search",
106
132
  "configurable search experience", "control over search index"
107
133
  ]
108
134
  weaknesses = [
109
- "requires API key with usage limits",
135
+ "requires API key with usage limits",
110
136
  "limited to 10,000 queries/day on free tier",
111
137
  "requires search engine configuration in Google Control Panel"
112
138
  ]
113
139
  supports_full_search = true
114
- full_search_module = "local_deep_research.web_search_engines.engines.full_search"
140
+ full_search_module = ".engines.full_search"
115
141
  full_search_class = "FullSearchResults"
116
142
 
117
143
  [google_pse.default_params]
@@ -120,13 +146,13 @@ safe_search = true
120
146
  search_language = "English"
121
147
 
122
148
  [brave]
123
- module_path = "local_deep_research.web_search_engines.engines.search_engine_brave"
149
+ module_path = ".engines.search_engine_brave"
124
150
  class_name = "BraveSearchEngine"
125
151
  requires_api_key = true
126
152
  api_key_env = "BRAVE_API_KEY"
127
153
  reliability = 0.7
128
154
  strengths = [
129
- "privacy-focused web search", "product information", "reviews",
155
+ "privacy-focused web search", "product information", "reviews",
130
156
  "recent content", "news", "broad coverage"
131
157
  ]
132
158
  weaknesses = ["requires API key with usage limits", "smaller index than Google"]
@@ -141,16 +167,16 @@ safe_search = true
141
167
  search_language = "English"
142
168
 
143
169
  [wayback]
144
- module_path = "local_deep_research.web_search_engines.engines.search_engine_wayback"
170
+ module_path = ".engines.search_engine_wayback"
145
171
  class_name = "WaybackSearchEngine"
146
172
  requires_api_key = false
147
173
  reliability = 0.5
148
174
  strengths = [
149
- "historical web content", "archived websites", "content verification",
175
+ "historical web content", "archived websites", "content verification",
150
176
  "deleted or changed web pages", "website evolution tracking"
151
177
  ]
152
178
  weaknesses = [
153
- "limited to previously archived content", "may miss recent changes",
179
+ "limited to previously archived content", "may miss recent changes",
154
180
  "archiving quality varies"
155
181
  ]
156
182
  supports_full_search = true
@@ -162,12 +188,12 @@ closest_only = false
162
188
  language = "English"
163
189
 
164
190
  [auto]
165
- module_path = "local_deep_research.web_search_engines.engines.meta_search_engine"
191
+ module_path = ".engines.meta_search_engine"
166
192
  class_name = "MetaSearchEngine"
167
193
  requires_api_key = false
168
194
  reliability = 0.85
169
195
  strengths = [
170
- "intelligent engine selection", "adaptable to query type",
196
+ "intelligent engine selection", "adaptable to query type",
171
197
  "fallback capabilities"
172
198
  ]
173
199
  weaknesses = ["slightly slower due to LLM analysis"]
@@ -178,7 +204,7 @@ use_api_key_services = true
178
204
  max_engines_to_try = 3
179
205
 
180
206
  [local_all]
181
- module_path = "local_deep_research.web_search_engines.engines.search_engine_local_all"
207
+ module_path = ".engines.search_engine_local_all"
182
208
  class_name = "LocalAllSearchEngine"
183
209
  requires_api_key = false
184
210
  reliability = 0.85
@@ -187,16 +213,16 @@ weaknesses = ["may return too many results", "requires indexing"]
187
213
  requires_llm = true
188
214
 
189
215
  #[semantic_scholar]
190
- #module_path = "local_deep_research.web_search_engines.engines.search_engine_semantic_scholar"
216
+ #module_path = ".engines.search_engine_semantic_scholar"
191
217
  #class_name = "SemanticScholarSearchEngine"
192
218
  #requires_api_key = false
193
219
  #api_key_env = "S2_API_KEY"
194
220
  #reliability = 0.87
195
221
  #strengths = [
196
- # "comprehensive scientific literature",
197
- # "extensive citation network",
198
- # "AI-generated summaries (TLDRs)",
199
- # "academic paper metadata",
222
+ # "comprehensive scientific literature",
223
+ # "extensive citation network",
224
+ # "AI-generated summaries (TLDRs)",
225
+ # "academic paper metadata",
200
226
  # "cross-disciplinary coverage",
201
227
  # "200M+ papers across all fields",
202
228
  # "usable without API key"
@@ -232,34 +258,34 @@ DEFAULT_SEARCH_ENGINE = "wikipedia"
232
258
  # Uncomment and modify these templates as needed
233
259
 
234
260
  # [duckduckgo]
235
- # module_path = "local_deep_research.web_search_engines.engines.search_engine_ddg"
261
+ # module_path = ".engines.search_engine_ddg"
236
262
  # class_name = "DuckDuckGoSearchEngine"
237
263
  # requires_api_key = false
238
264
  # reliability = 0.4
239
265
  # strengths = [
240
- # "web search", "product information", "reviews", "recent information",
266
+ # "web search", "product information", "reviews", "recent information",
241
267
  # "news", "general queries", "broad coverage"
242
268
  # ]
243
269
  # weaknesses = ["inconsistent due to rate limits", "not specialized for academic content"]
244
270
  # supports_full_search = true
245
271
  # full_search_module = "local_deep_research.web_search_engines.engines.full_search"
246
272
  # full_search_class = "FullSearchResults"
247
- #
273
+ #
248
274
  # [duckduckgo.default_params]
249
275
  # region = "us"
250
276
  # safe_search = true
251
277
 
252
278
  # [guardian]
253
- # module_path = "local_deep_research.web_search_engines.engines.search_engine_guardian"
279
+ # module_path = ".engines.search_engine_guardian"
254
280
  # class_name = "GuardianSearchEngine"
255
281
  # requires_api_key = true
256
282
  # api_key_env = "GUARDIAN_API_KEY"
257
283
  # reliability = 0.5
258
284
  # strengths = [
259
- # "news articles", "current events", "opinion pieces", "journalism",
285
+ # "news articles", "current events", "opinion pieces", "journalism",
260
286
  # "UK and global news", "political analysis"
261
287
  # ]
262
288
  # weaknesses = ["primarily focused on news", "limited historical content pre-1999"]
263
- #
289
+ #
264
290
  # [guardian.default_params]
265
291
  # order_by = "relevance"
@@ -1,6 +1,11 @@
1
- from .search_system import AdvancedSearchSystem
1
+ import logging
2
+ import sys
2
3
  from typing import Dict
3
- from .config import settings
4
+
5
+ from . import get_advanced_search_system, get_report_generator
6
+ from .config.config_files import settings
7
+ from .utilities.db_utils import get_db_setting
8
+
4
9
 
5
10
  def print_report(report: Dict):
6
11
  """Print and save the report in a readable format"""
@@ -11,8 +16,6 @@ def print_report(report: Dict):
11
16
  # Print content
12
17
  print(report["content"])
13
18
 
14
-
15
-
16
19
  # Save to file in markdown format
17
20
  with open("report.md", "w", encoding="utf-8") as markdown_file:
18
21
  # Write content
@@ -24,31 +27,40 @@ def print_report(report: Dict):
24
27
 
25
28
  markdown_file.write(f"- Query: {report['metadata']['query']}\n")
26
29
 
27
- print(f"\nReport has been saved to report.md")
30
+ print("\nReport has been saved to report.md")
28
31
 
29
32
 
30
- from .report_generator import IntegratedReportGenerator
33
+ # Create the report generator lazily to avoid circular imports
34
+ def get_report_generator_instance():
35
+ return get_report_generator()
31
36
 
32
- report_generator = IntegratedReportGenerator()
33
37
 
38
+ # report_generator = IntegratedReportGenerator()
39
+ report_generator = None # Will be initialized when needed
34
40
 
35
41
 
36
42
  def main():
37
- import os
38
43
  import logging
39
- from .utilties.setup_utils import setup_user_directories
40
-
44
+
45
+ from .utilities.setup_utils import setup_user_directories
46
+
41
47
  # Configure logging
42
48
  logging.basicConfig(level=logging.INFO)
43
49
  logger = logging.getLogger(__name__)
44
- logger.info(f"Starting with settings: iterations={settings.search.iterations}, "
45
- f"questions_per_iteration={settings.search.questions_per_iteration}")
46
-
50
+ search_iterations = get_db_setting("search.iterations", settings.search.iterations)
51
+ questions_per_iteration = get_db_setting(
52
+ "search.questions_per_iteration", settings.search.questions_per_iteration
53
+ )
54
+ logger.info(
55
+ f"Starting with settings: iterations={search_iterations}, "
56
+ f"questions_per_iteration={questions_per_iteration}"
57
+ )
58
+
47
59
  # Explicitly run setup
48
60
  logger.info("Initializing configuration...")
49
61
  setup_user_directories()
50
-
51
- system = AdvancedSearchSystem()
62
+
63
+ system = get_advanced_search_system()
52
64
 
53
65
  print("Welcome to the Advanced Research System")
54
66
  print("Type 'quit' to exit")
@@ -97,9 +109,12 @@ def main():
97
109
 
98
110
  else:
99
111
  # Full Report
100
- final_report = report_generator.generate_report(
101
- results, query
102
- )
112
+ # Initialize report_generator if not already done
113
+ global report_generator
114
+ if report_generator is None:
115
+ report_generator = get_report_generator()
116
+
117
+ final_report = report_generator.generate_report(results, query)
103
118
  print("\n=== RESEARCH REPORT ===")
104
119
  print_report(final_report)
105
120
 
@@ -109,5 +124,92 @@ def main():
109
124
  else:
110
125
  print("Research failed. Please try again.")
111
126
 
127
+
128
+ # Add command for database migration
112
129
  if __name__ == "__main__":
113
- main()
130
+ import argparse
131
+
132
+ parser = argparse.ArgumentParser(description="Local Deep Research")
133
+ parser.add_argument("--web", action="store_true", help="Start the web server")
134
+ parser.add_argument(
135
+ "--migrate-db", action="store_true", help="Migrate legacy databases to ldr.db"
136
+ )
137
+ parser.add_argument("--debug", action="store_true", help="Enable debug mode")
138
+ parser.add_argument(
139
+ "--test-migration",
140
+ action="store_true",
141
+ help="Test migration by checking database contents",
142
+ )
143
+ parser.add_argument(
144
+ "--schema-upgrade",
145
+ action="store_true",
146
+ help="Run schema upgrades on the database (e.g., remove redundant tables)",
147
+ )
148
+
149
+ args = parser.parse_args()
150
+
151
+ if args.debug:
152
+ logging.basicConfig(level=logging.DEBUG)
153
+ else:
154
+ logging.basicConfig(level=logging.INFO)
155
+
156
+ if args.migrate_db:
157
+ try:
158
+ # First ensure data directory exists
159
+ from src.local_deep_research.setup_data_dir import setup_data_dir
160
+
161
+ setup_data_dir()
162
+
163
+ # Then run the migration
164
+ from src.local_deep_research.web.database.migrate_to_ldr_db import (
165
+ migrate_to_ldr_db,
166
+ )
167
+
168
+ print("Starting database migration...")
169
+ success = migrate_to_ldr_db()
170
+ if success:
171
+ print("Database migration completed successfully")
172
+ sys.exit(0)
173
+ else:
174
+ print("Database migration failed")
175
+ sys.exit(1)
176
+ except Exception as e:
177
+ print(f"Error running database migration: {e}")
178
+ sys.exit(1)
179
+
180
+ if args.test_migration:
181
+ try:
182
+ from src.local_deep_research.test_migration import main as test_main
183
+
184
+ sys.exit(test_main())
185
+ except Exception as e:
186
+ print(f"Error running migration test: {e}")
187
+ sys.exit(1)
188
+
189
+ if args.schema_upgrade:
190
+ try:
191
+ from src.local_deep_research.web.database.schema_upgrade import (
192
+ run_schema_upgrades,
193
+ )
194
+
195
+ print("Running database schema upgrades...")
196
+ success = run_schema_upgrades()
197
+ if success:
198
+ print("Schema upgrades completed successfully")
199
+ sys.exit(0)
200
+ else:
201
+ print("Schema upgrades failed")
202
+ sys.exit(1)
203
+ except Exception as e:
204
+ print(f"Error running schema upgrades: {e}")
205
+ sys.exit(1)
206
+
207
+ if args.web:
208
+ from src.local_deep_research.web.app import main as web_main
209
+
210
+ web_main()
211
+ else:
212
+ # Default to web if no command specified
213
+ from src.local_deep_research.web.app import main as web_main
214
+
215
+ web_main()
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Database migration script for Local Deep Research.
4
+ Migrates data from legacy databases (deep_research.db and research_history.db) to the new unified database (ldr.db).
5
+ """
6
+
7
+ import argparse
8
+ import logging
9
+ import os
10
+ import sys
11
+
12
+ try:
13
+ from local_deep_research.web.database.migrate_to_ldr_db import (
14
+ migrate_to_ldr_db,
15
+ )
16
+ from local_deep_research.web.models.database import (
17
+ DB_PATH,
18
+ LEGACY_DEEP_RESEARCH_DB,
19
+ LEGACY_RESEARCH_HISTORY_DB,
20
+ )
21
+ except ImportError:
22
+ # If that fails, try with the relative path.
23
+ from .web.database.migrate_to_ldr_db import migrate_to_ldr_db
24
+ from .web.models.database import (
25
+ DB_PATH,
26
+ LEGACY_DEEP_RESEARCH_DB,
27
+ LEGACY_RESEARCH_HISTORY_DB,
28
+ )
29
+
30
+ # Configure logging
31
+ logging.basicConfig(
32
+ level=logging.INFO,
33
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
34
+ handlers=[logging.StreamHandler()],
35
+ )
36
+ logger = logging.getLogger("migrate_db")
37
+
38
+ # Add proper paths for import
39
+ current_dir = os.path.dirname(os.path.abspath(__file__))
40
+ sys.path.insert(0, os.path.dirname(os.path.dirname(current_dir)))
41
+
42
+
43
+ def main():
44
+ """Main migration function that parses arguments and runs the migration"""
45
+ parser = argparse.ArgumentParser(
46
+ description="Local Deep Research Database Migration"
47
+ )
48
+ parser.add_argument(
49
+ "--backup",
50
+ action="store_true",
51
+ help="Create backup of existing databases before migration",
52
+ )
53
+ parser.add_argument(
54
+ "--force",
55
+ action="store_true",
56
+ help="Force migration even if target database exists",
57
+ )
58
+ parser.add_argument(
59
+ "--dry-run",
60
+ action="store_true",
61
+ help="Only check what would be migrated, don't perform actual migration",
62
+ )
63
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
64
+
65
+ args = parser.parse_args()
66
+
67
+ if args.verbose:
68
+ logging.getLogger().setLevel(logging.DEBUG)
69
+
70
+ print("=" * 80)
71
+ print("LOCAL DEEP RESEARCH DATABASE MIGRATION")
72
+ print("=" * 80)
73
+
74
+ try:
75
+ # First try the normal import
76
+ print(f"Target database will be created at: {DB_PATH}")
77
+
78
+ # Check if migration is needed
79
+ if os.path.exists(DB_PATH) and not args.force:
80
+ print(f"Target database already exists at: {DB_PATH}")
81
+ if (
82
+ input(
83
+ "Do you want to continue anyway? This may overwrite data. (y/n): "
84
+ ).lower()
85
+ != "y"
86
+ ):
87
+ print("Migration aborted.")
88
+ return 1
89
+
90
+ # Check if source databases exist
91
+ deep_research_exists = os.path.exists(LEGACY_DEEP_RESEARCH_DB)
92
+ research_history_exists = os.path.exists(LEGACY_RESEARCH_HISTORY_DB)
93
+
94
+ if not deep_research_exists and not research_history_exists:
95
+ print("No legacy databases found. Nothing to migrate.")
96
+ return 0
97
+
98
+ print("Found legacy databases:")
99
+ if deep_research_exists:
100
+ print(f" - {LEGACY_DEEP_RESEARCH_DB}")
101
+ if research_history_exists:
102
+ print(f" - {LEGACY_RESEARCH_HISTORY_DB}")
103
+
104
+ # Create backups if requested
105
+ if args.backup:
106
+ if deep_research_exists:
107
+ backup_path = f"{LEGACY_DEEP_RESEARCH_DB}.bak"
108
+ import shutil
109
+
110
+ shutil.copy2(LEGACY_DEEP_RESEARCH_DB, backup_path)
111
+ print(f"Created backup: {backup_path}")
112
+
113
+ if research_history_exists:
114
+ backup_path = f"{LEGACY_RESEARCH_HISTORY_DB}.bak"
115
+ import shutil
116
+
117
+ shutil.copy2(LEGACY_RESEARCH_HISTORY_DB, backup_path)
118
+ print(f"Created backup: {backup_path}")
119
+
120
+ # Run migration or dry run
121
+ if args.dry_run:
122
+ print("\nDRY RUN - No changes will be made.")
123
+ print(f"Would migrate data to: {DB_PATH}")
124
+ return 0
125
+ else:
126
+ print(f"\nStarting migration to: {DB_PATH}")
127
+
128
+ success = migrate_to_ldr_db()
129
+
130
+ if success:
131
+ print("\nMigration completed successfully.")
132
+ print(
133
+ "You can now start the application with the new unified database."
134
+ )
135
+ return 0
136
+ else:
137
+ print("\nMigration failed. Check the logs for details.")
138
+ return 1
139
+
140
+ except Exception as e:
141
+ logger.error(f"Migration error: {e}", exc_info=True)
142
+ print(f"Error during migration: {e}")
143
+ return 1
144
+
145
+
146
+ if __name__ == "__main__":
147
+ sys.exit(main())