local-deep-research 0.3.6__tar.gz → 0.3.9__tar.gz

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 (143) hide show
  1. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/PKG-INFO +13 -7
  2. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/README.md +12 -6
  3. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/pyproject.toml +1 -1
  4. local_deep_research-0.3.9/src/local_deep_research/__version__.py +1 -0
  5. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/defaults/default_settings.json +9 -9
  6. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/search_system.py +5 -2
  7. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/db_utils.py +5 -9
  8. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/services/settings_manager.py +12 -5
  9. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_searxng.py +17 -22
  10. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/search_engines_config.py +16 -9
  11. local_deep_research-0.3.9/tests/fix_tests/README.md +36 -0
  12. local_deep_research-0.3.9/tests/fix_tests/test_duplicate_links_fix.py +102 -0
  13. local_deep_research-0.3.6/src/local_deep_research/__version__.py +0 -1
  14. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/LICENSE +0 -0
  15. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/__init__.py +0 -0
  16. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/__main__.py +0 -0
  17. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/__init__.py +0 -0
  18. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/filters/__init__.py +0 -0
  19. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/filters/base_filter.py +0 -0
  20. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/filters/cross_engine_filter.py +0 -0
  21. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/findings/base_findings.py +0 -0
  22. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/findings/repository.py +0 -0
  23. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/knowledge/__init__.py +0 -0
  24. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/knowledge/base_knowledge.py +0 -0
  25. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +0 -0
  26. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/questions/__init__.py +0 -0
  27. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/questions/base_question.py +0 -0
  28. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/questions/decomposition_question.py +0 -0
  29. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/questions/standard_question.py +0 -0
  30. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/repositories/__init__.py +0 -0
  31. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/__init__.py +0 -0
  32. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/base_strategy.py +0 -0
  33. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +0 -0
  34. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +0 -0
  35. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +0 -0
  36. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/source_based_strategy.py +0 -0
  37. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/strategies/standard_strategy.py +0 -0
  38. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/tools/__init__.py +0 -0
  39. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/tools/base_tool.py +0 -0
  40. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +0 -0
  41. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/tools/question_tools/__init__.py +0 -0
  42. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/advanced_search_system/tools/search_tools/__init__.py +0 -0
  43. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/api/__init__.py +0 -0
  44. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/api/research_functions.py +0 -0
  45. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/app.py +0 -0
  46. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/citation_handler.py +0 -0
  47. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/config/__init__.py +0 -0
  48. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/config/llm_config.py +0 -0
  49. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/config/search_config.py +0 -0
  50. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/defaults/.env.template +0 -0
  51. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/defaults/__init__.py +0 -0
  52. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/main.py +0 -0
  53. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/migrate_db.py +0 -0
  54. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/report_generator.py +0 -0
  55. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/setup_data_dir.py +0 -0
  56. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/test_migration.py +0 -0
  57. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/__init__.py +0 -0
  58. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/enums.py +0 -0
  59. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/llm_utils.py +0 -0
  60. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/search_utilities.py +0 -0
  61. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/utilities/setup_utils.py +0 -0
  62. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/__init__.py +0 -0
  63. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/app.py +0 -0
  64. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/app_factory.py +0 -0
  65. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/database/README.md +0 -0
  66. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/database/migrate_to_ldr_db.py +0 -0
  67. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/database/migrations.py +0 -0
  68. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/database/models.py +0 -0
  69. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/database/schema_upgrade.py +0 -0
  70. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/models/database.py +0 -0
  71. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/models/settings.py +0 -0
  72. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/routes/api_routes.py +0 -0
  73. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/routes/history_routes.py +0 -0
  74. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/routes/research_routes.py +0 -0
  75. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/routes/settings_routes.py +0 -0
  76. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/services/research_service.py +0 -0
  77. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/services/resource_service.py +0 -0
  78. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/services/settings_service.py +0 -0
  79. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/services/socket_service.py +0 -0
  80. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/css/custom_dropdown.css +0 -0
  81. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/css/settings.css +0 -0
  82. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/css/styles.css +0 -0
  83. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/custom_dropdown.js +0 -0
  84. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/detail.js +0 -0
  85. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/fallback/formatting.js +0 -0
  86. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/fallback/ui.js +0 -0
  87. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/history.js +0 -0
  88. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/logpanel.js +0 -0
  89. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/progress.js +0 -0
  90. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/research.js +0 -0
  91. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/results.js +0 -0
  92. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/settings.js +0 -0
  93. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/components/settings_sync.js +0 -0
  94. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/main.js +0 -0
  95. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/research_form.js +0 -0
  96. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/api.js +0 -0
  97. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/audio.js +0 -0
  98. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/formatting.js +0 -0
  99. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/pdf.js +0 -0
  100. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/socket.js +0 -0
  101. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/js/services/ui.js +0 -0
  102. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/sounds/README.md +0 -0
  103. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/sounds/error.mp3 +0 -0
  104. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/static/sounds/success.mp3 +0 -0
  105. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/base.html +0 -0
  106. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/components/custom_dropdown.html +0 -0
  107. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/components/log_panel.html +0 -0
  108. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/components/mobile_nav.html +0 -0
  109. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/components/settings_form.html +0 -0
  110. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/components/sidebar.html +0 -0
  111. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/pages/details.html +0 -0
  112. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/pages/history.html +0 -0
  113. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/pages/progress.html +0 -0
  114. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/pages/research.html +0 -0
  115. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/pages/results.html +0 -0
  116. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/templates/settings_dashboard.html +0 -0
  117. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/utils/__init__.py +0 -0
  118. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/utils/formatters.py +0 -0
  119. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web/utils/templates.py +0 -0
  120. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/__init__.py +0 -0
  121. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/__init__.py +0 -0
  122. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/full_search.py +0 -0
  123. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/meta_search_engine.py +0 -0
  124. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_arxiv.py +0 -0
  125. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_brave.py +0 -0
  126. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_ddg.py +0 -0
  127. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_github.py +0 -0
  128. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_google_pse.py +0 -0
  129. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_guardian.py +0 -0
  130. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_local.py +0 -0
  131. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_local_all.py +0 -0
  132. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_pubmed.py +0 -0
  133. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +0 -0
  134. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_serpapi.py +0 -0
  135. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_wayback.py +0 -0
  136. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +0 -0
  137. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/search_engine_base.py +0 -0
  138. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/src/local_deep_research/web_search_engines/search_engine_factory.py +0 -0
  139. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/tests/__init__.py +0 -0
  140. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/tests/download_stuff_for_local_test.py +0 -0
  141. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/tests/searxng/test_searxng_instance.py +0 -0
  142. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/tests/searxng/test_searxng_integration.py +0 -0
  143. {local_deep_research-0.3.6 → local_deep_research-0.3.9}/tests/test_google_pse.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: local-deep-research
3
- Version: 0.3.6
3
+ Version: 0.3.9
4
4
  Summary: AI-powered research assistant with deep, iterative analysis using LLMs and web searches
5
5
  Author-Email: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com>, HashedViking <6432677+HashedViking@users.noreply.github.com>
6
6
  License: MIT License
@@ -80,9 +80,14 @@ Description-Content-Type: text/markdown
80
80
 
81
81
  *AI-powered research assistant that performs deep, iterative analysis using multiple LLMs and web searches*
82
82
 
83
- <a href="https://www.youtube.com/watch?v=0ISreg9q0p0">
84
- <img src="https://img.youtube.com/vi/0ISreg9q0p0/0.jpg" alt="Local Deep Research Demo" width="500">
85
- </a>
83
+ <div align="center">
84
+ <a href="https://www.youtube.com/watch?v=0ISreg9q0p0">
85
+ <img src="https://img.youtube.com/vi/0ISreg9q0p0/0.jpg" alt="Local Deep Research">
86
+ <br>
87
+ <span>▶️ Watch Video</span>
88
+ </a>
89
+ </div>
90
+
86
91
 
87
92
  </div>
88
93
 
@@ -167,6 +172,7 @@ print(results["summary"])
167
172
 
168
173
  **Windows**: Docker is the easiest option for Windows users. If preferred, a [Windows Installer](https://github.com/LearningCircuit/local-deep-research/releases/download/v0.1.0/LocalDeepResearch_Setup.exe) is also available.
169
174
 
175
+ For more information on installation options, see [the wiki](https://github.com/LearningCircuit/local-deep-research/wiki/Installation).
170
176
 
171
177
  ## 🔍 Research Capabilities
172
178
 
@@ -273,9 +279,9 @@ For enhanced web search capabilities, you can configure these additional engines
273
279
 
274
280
  ```bash
275
281
  # Search API keys (if not using the web UI)
276
- SERP_API_KEY=your-key-here # Google results via SerpAPI
277
- GOOGLE_PSE_API_KEY=your-key-here # Google Programmable Search
278
- BRAVE_API_KEY=your-key-here # Brave Search
282
+ LDR_SEARCH_ENGINE_WEB_SERPAPI_API_KEY=your-key-here # Google results via SerpAPI
283
+ LDR_SEARCH_ENGINE_WEB_GOOGLE_PSE_API_KEY=your-key-here # Google Programmable Search
284
+ LDR_SEARCH_ENGINE_WEB_BRAVE_API_KEY=your-key-here # Brave Search
279
285
  ```
280
286
 
281
287
  ### Search Engine Comparison
@@ -9,9 +9,14 @@
9
9
 
10
10
  *AI-powered research assistant that performs deep, iterative analysis using multiple LLMs and web searches*
11
11
 
12
- <a href="https://www.youtube.com/watch?v=0ISreg9q0p0">
13
- <img src="https://img.youtube.com/vi/0ISreg9q0p0/0.jpg" alt="Local Deep Research Demo" width="500">
14
- </a>
12
+ <div align="center">
13
+ <a href="https://www.youtube.com/watch?v=0ISreg9q0p0">
14
+ <img src="https://img.youtube.com/vi/0ISreg9q0p0/0.jpg" alt="Local Deep Research">
15
+ <br>
16
+ <span>▶️ Watch Video</span>
17
+ </a>
18
+ </div>
19
+
15
20
 
16
21
  </div>
17
22
 
@@ -96,6 +101,7 @@ print(results["summary"])
96
101
 
97
102
  **Windows**: Docker is the easiest option for Windows users. If preferred, a [Windows Installer](https://github.com/LearningCircuit/local-deep-research/releases/download/v0.1.0/LocalDeepResearch_Setup.exe) is also available.
98
103
 
104
+ For more information on installation options, see [the wiki](https://github.com/LearningCircuit/local-deep-research/wiki/Installation).
99
105
 
100
106
  ## 🔍 Research Capabilities
101
107
 
@@ -202,9 +208,9 @@ For enhanced web search capabilities, you can configure these additional engines
202
208
 
203
209
  ```bash
204
210
  # Search API keys (if not using the web UI)
205
- SERP_API_KEY=your-key-here # Google results via SerpAPI
206
- GOOGLE_PSE_API_KEY=your-key-here # Google Programmable Search
207
- BRAVE_API_KEY=your-key-here # Brave Search
211
+ LDR_SEARCH_ENGINE_WEB_SERPAPI_API_KEY=your-key-here # Google results via SerpAPI
212
+ LDR_SEARCH_ENGINE_WEB_GOOGLE_PSE_API_KEY=your-key-here # Google Programmable Search
213
+ LDR_SEARCH_ENGINE_WEB_BRAVE_API_KEY=your-key-here # Brave Search
208
214
  ```
209
215
 
210
216
  ### Search Engine Comparison
@@ -57,7 +57,7 @@ dependencies = [
57
57
  "setuptools>=78.1.0",
58
58
  "flask-wtf>=1.2.2",
59
59
  ]
60
- version = "0.3.6"
60
+ version = "0.3.9"
61
61
 
62
62
  [project.license]
63
63
  file = "LICENSE"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.9"
@@ -2927,32 +2927,32 @@
2927
2927
  "value": "A locally-hosted meta-search engine.",
2928
2928
  "visible": false
2929
2929
  },
2930
- "search.engine.web.searxng.api_key": {
2930
+ "search.engine.web.searxng.class_name": {
2931
2931
  "category": "searxng",
2932
- "description": "The SearXNG API key to use.",
2932
+ "description": "Setting for searxng.class_name",
2933
2933
  "editable": true,
2934
2934
  "max_value": null,
2935
2935
  "min_value": null,
2936
- "name": "Api Key",
2936
+ "name": "Class Name",
2937
2937
  "options": null,
2938
2938
  "step": null,
2939
2939
  "type": "SEARCH",
2940
- "ui_element": "password",
2941
- "value": "SEARXNG_API_KEY",
2940
+ "ui_element": "text",
2941
+ "value": "SearXNGSearchEngine",
2942
2942
  "visible": true
2943
2943
  },
2944
- "search.engine.web.searxng.class_name": {
2944
+ "search.engine.web.searxng.default_params.instance_url": {
2945
2945
  "category": "searxng",
2946
- "description": "Setting for searxng.class_name",
2946
+ "description": "The SearXNG API endpoint URL.",
2947
2947
  "editable": true,
2948
2948
  "max_value": null,
2949
2949
  "min_value": null,
2950
- "name": "Class Name",
2950
+ "name": "Endpoint URL",
2951
2951
  "options": null,
2952
2952
  "step": null,
2953
2953
  "type": "SEARCH",
2954
2954
  "ui_element": "text",
2955
- "value": "SearXNGSearchEngine",
2955
+ "value": "http://localhost:8080",
2956
2956
  "visible": true
2957
2957
  },
2958
2958
  "search.engine.web.searxng.default_params.categories": {
@@ -180,8 +180,11 @@ class AdvancedSearchSystem:
180
180
  self.questions_by_iteration = self.strategy.questions_by_iteration.copy()
181
181
  # Send progress message with search info
182
182
 
183
- # if hasattr(self.strategy, "all_links_of_system"):
184
- self.all_links_of_system.extend(self.strategy.all_links_of_system)
183
+ # Only extend if they're different objects in memory to avoid duplication
184
+ # This check prevents doubling the list when they reference the same object
185
+ # Fix for issue #301: "too many links in detailed report mode"
186
+ if id(self.all_links_of_system) != id(self.strategy.all_links_of_system):
187
+ self.all_links_of_system.extend(self.strategy.all_links_of_system)
185
188
 
186
189
  # Include the search system instance for access to citations
187
190
  result["search_system"] = self
@@ -6,7 +6,7 @@ from typing import Any, Dict
6
6
  from sqlalchemy import create_engine
7
7
  from sqlalchemy.orm import Session, sessionmaker
8
8
 
9
- from ..web.services.settings_manager import SettingsManager, check_env_setting
9
+ from ..web.services.settings_manager import SettingsManager
10
10
 
11
11
  logger = logging.getLogger(__name__)
12
12
 
@@ -38,7 +38,7 @@ def get_settings_manager() -> SettingsManager:
38
38
 
39
39
 
40
40
  def get_db_setting(
41
- key: str, default_value: Any | None = None, check_env: bool = True
41
+ key: str, default_value: Any | None = None
42
42
  ) -> str | Dict[str, Any] | None:
43
43
  """
44
44
  Get a setting from the database with fallback to default value
@@ -46,15 +46,11 @@ def get_db_setting(
46
46
  Args:
47
47
  key: The setting key.
48
48
  default_value: If the setting is not found, it will return this instead.
49
- check_env: If true, it will check the corresponding environment
50
- variable before checking the DB and return that if it is set.
51
49
 
52
- """
53
- if check_env:
54
- env_value = check_env_setting(key)
55
- if env_value is not None:
56
- return env_value
50
+ Returns:
51
+ The setting value.
57
52
 
53
+ """
58
54
  try:
59
55
  # Get settings manager which handles database access
60
56
  value = get_settings_manager().get_setting(key)
@@ -94,11 +94,18 @@ class SettingsManager:
94
94
  return value
95
95
  elif len(settings) > 1:
96
96
  # This is a higher-level key.
97
- settings_map = {
98
- s.key.removeprefix(f"{key}."): s.value for s in settings
99
- }
100
- # We deliberately don't update the cache here to avoid
101
- # conflicts between low-level keys and their parent keys.
97
+ settings_map = {}
98
+ for setting in settings:
99
+ output_key = setting.key.removeprefix(f"{key}.")
100
+ value = setting.value
101
+
102
+ if check_env:
103
+ # Handle possible replacements from environment variables.
104
+ env_value = check_env_setting(setting.key)
105
+ if env_value is not None:
106
+ value = env_value
107
+
108
+ settings_map[output_key] = value
102
109
  return settings_map
103
110
  except SQLAlchemyError as e:
104
111
  logger.error(f"Error retrieving setting {key} from database: {e}")
@@ -25,7 +25,7 @@ class SearXNGSearchEngine(BaseSearchEngine):
25
25
  def __init__(
26
26
  self,
27
27
  max_results: int = 15,
28
- instance_url: Optional[str] = None, # Can be None if using env var
28
+ instance_url: str = "http://localhost:8080",
29
29
  categories: Optional[List[str]] = None,
30
30
  engines: Optional[List[str]] = None,
31
31
  language: str = "en",
@@ -35,7 +35,6 @@ class SearXNGSearchEngine(BaseSearchEngine):
35
35
  llm: Optional[BaseLLM] = None,
36
36
  max_filtered_results: Optional[int] = None,
37
37
  include_full_content: bool = True,
38
- api_key: Optional[str] = None,
39
38
  ): # API key is actually the instance URL
40
39
  """
41
40
  Initialize the SearXNG search engine with ethical usage patterns.
@@ -52,7 +51,6 @@ class SearXNGSearchEngine(BaseSearchEngine):
52
51
  llm: Language model for relevance filtering
53
52
  max_filtered_results: Maximum number of results to keep after filtering
54
53
  include_full_content: Whether to include full webpage content in results
55
- api_key: Alternative way to provide instance URL (takes precedence over instance_url)
56
54
  """
57
55
 
58
56
  # Initialize the BaseSearchEngine with LLM, max_filtered_results, and max_results
@@ -60,28 +58,25 @@ class SearXNGSearchEngine(BaseSearchEngine):
60
58
  llm=llm, max_filtered_results=max_filtered_results, max_results=max_results
61
59
  )
62
60
 
63
- # Get instance URL from various sources in priority order:
64
- # 1. api_key parameter (which is actually the instance URL)
65
- # 2. SEARXNG_INSTANCE environment variable
66
- # 3. instance_url parameter
67
- # 4. Default to None, which will disable the engine
68
- self.instance_url = api_key or os.getenv("SEARXNG_INSTANCE") or instance_url or "http://localhost:8080"
69
-
70
- # Add debug logging for instance URL
71
- logger.info(
72
- f"SearXNG init - Instance URL sources: api_key={api_key}, env={os.getenv('SEARXNG_INSTANCE')}, param={instance_url}"
73
- )
74
-
61
+ self.instance_url = instance_url
75
62
  # Validate and normalize the instance URL if provided
76
- if self.instance_url:
77
- self.instance_url = self.instance_url.rstrip("/")
78
- self.is_available = True
79
- logger.info(f"SearXNG initialized with instance URL: {self.instance_url}")
80
- else:
63
+ self.instance_url = self.instance_url.rstrip("/")
64
+ logger.info(f"SearXNG initialized with instance URL: {self.instance_url}")
65
+ try:
66
+ # Make sure it's accessible.
67
+ response = requests.get(self.instance_url, timeout=5)
68
+ if response.status_code == 200:
69
+ logger.info("SearXNG instance is accessible.")
70
+ self.is_available = True
71
+ else:
72
+ self.is_available = False
73
+ logger.error(
74
+ f"Failed to access SearXNG instance at {self.instance_url}. Status code: {response.status_code}"
75
+ )
76
+ except requests.RequestException as e:
81
77
  self.is_available = False
82
78
  logger.error(
83
- "No SearXNG instance URL provided. The engine is disabled. "
84
- "Set SEARXNG_INSTANCE environment variable or provide instance_url parameter."
79
+ f"Error while trying to access SearXNG instance at {self.instance_url}: {str(e)}"
85
80
  )
86
81
 
87
82
  # Add debug logging for all parameters
@@ -5,7 +5,6 @@ Loads search engine definitions from the user's configuration.
5
5
 
6
6
  import json
7
7
  import logging
8
- from functools import cache
9
8
  from typing import Any, Dict, List
10
9
 
11
10
  from ..utilities.db_utils import get_db_setting
@@ -25,16 +24,26 @@ def _extract_per_engine_config(raw_config: Dict[str, Any]) -> Dict[str, Dict[str
25
24
  Configuration dictionaries indexed by engine name.
26
25
 
27
26
  """
28
- engine_config = {}
27
+ nested_config = {}
29
28
  for key, value in raw_config.items():
30
- engine_name = key.split(".")[0]
31
- setting_name = ".".join(key.split(".")[1:])
32
- engine_config.setdefault(engine_name, {})[setting_name] = value
29
+ if "." in key:
30
+ # This is a higher-level key.
31
+ top_level_key = key.split(".")[0]
32
+ lower_keys = ".".join(key.split(".")[1:])
33
+ nested_config.setdefault(top_level_key, {})[lower_keys] = value
34
+ else:
35
+ # This is a low-level key.
36
+ nested_config[key] = value
33
37
 
34
- return engine_config
38
+ # Expand all the lower-level keys.
39
+ for key, value in nested_config.items():
40
+ if isinstance(value, dict):
41
+ # Expand the child keys.
42
+ nested_config[key] = _extract_per_engine_config(value)
43
+
44
+ return nested_config
35
45
 
36
46
 
37
- @cache
38
47
  def search_config() -> Dict[str, Any]:
39
48
  """
40
49
  Returns:
@@ -105,7 +114,6 @@ def search_config() -> Dict[str, Any]:
105
114
  return search_engines
106
115
 
107
116
 
108
- @cache
109
117
  def default_search_engine() -> str:
110
118
  """
111
119
  Returns:
@@ -115,7 +123,6 @@ def default_search_engine() -> str:
115
123
  return get_db_setting("search.engine.DEFAULT_SEARCH_ENGINE", "wikipedia")
116
124
 
117
125
 
118
- @cache
119
126
  def local_search_engines() -> List[str]:
120
127
  """
121
128
  Returns:
@@ -0,0 +1,36 @@
1
+ # Fix Tests
2
+
3
+ This directory contains tests for specific bug fixes in the codebase.
4
+
5
+ ## Tests
6
+
7
+ ### test_duplicate_links_fix.py
8
+
9
+ Demonstrates and tests the fix for GitHub issue [#301](https://github.com/LearningCircuit/local-deep-research/issues/301) - "too many links in detailed report mode".
10
+
11
+ The test simulates the bug where links are duplicated in the `all_links_of_system` list when using detailed report mode. It compares the behavior before and after implementing the fix.
12
+
13
+ #### Bug Description
14
+
15
+ The bug occurred because `search_system.py` was unconditionally extending `self.all_links_of_system` with `self.strategy.all_links_of_system` in the `analyze_topic` method, even though they were the same list object (when initialized with the same reference).
16
+
17
+ #### Fix
18
+
19
+ The fix checks if the lists are the same object (have the same `id()`) before extending:
20
+
21
+ ```python
22
+ # Only extend if they're different objects in memory to avoid duplication
23
+ if id(self.all_links_of_system) != id(self.strategy.all_links_of_system):
24
+ self.all_links_of_system.extend(self.strategy.all_links_of_system)
25
+ ```
26
+
27
+ This prevents duplicating the content when both lists are actually the same object.
28
+
29
+ #### Running the Test
30
+
31
+ ```bash
32
+ cd tests/fix_tests
33
+ python test_duplicate_links_fix.py
34
+ ```
35
+
36
+ The test demonstrates both the bug and the fixed behavior side-by-side.
@@ -0,0 +1,102 @@
1
+ """
2
+ Test script to demonstrate the duplicate links issue and our fix for issue #301.
3
+
4
+ This test demonstrates the problem where the search system duplicates links in detailed report mode
5
+ by unconditionally extending the all_links_of_system list with itself.
6
+
7
+ GitHub issue: https://github.com/LearningCircuit/local-deep-research/issues/301
8
+
9
+ The fix is to check if the lists are the same object (have the same id()) before extending.
10
+ """
11
+
12
+ class Strategy:
13
+ def __init__(self, all_links=None):
14
+ # Create a new list if None is provided
15
+ self.all_links_of_system = [] if all_links is None else all_links
16
+ print(f"Strategy initialized with list id: {id(self.all_links_of_system)}")
17
+
18
+ def analyze_topic(self, query):
19
+ # Add some links
20
+ self.all_links_of_system.extend([
21
+ {"title": "Link 1", "link": "http://example.com/1"},
22
+ {"title": "Link 2", "link": "http://example.com/2"},
23
+ ])
24
+ print(f"Strategy now has {len(self.all_links_of_system)} links")
25
+ return {"content": "Analysis results"}
26
+
27
+ class AdvancedSearchSystem:
28
+ def __init__(self):
29
+ # Initialize with empty list
30
+ self.all_links_of_system = []
31
+ print(f"Search system initialized with list id: {id(self.all_links_of_system)}")
32
+
33
+ # Create strategy with our list reference
34
+ self.strategy = Strategy(all_links=self.all_links_of_system)
35
+
36
+ # Check if they're the same object
37
+ print(f"Are lists the same object? {id(self.all_links_of_system) == id(self.strategy.all_links_of_system)}")
38
+
39
+ def analyze_topic_with_bug(self, query):
40
+ # Run the strategy
41
+ result = self.strategy.analyze_topic(query)
42
+
43
+ # BUG: Unconditionally extend our list with the strategy's list
44
+ # This is problematic because they're the same list
45
+ print("\nBUG DEMO: Extending unconditionally")
46
+ before_count = len(self.all_links_of_system)
47
+ print(f"Before extending: {before_count} links")
48
+
49
+ self.all_links_of_system.extend(self.strategy.all_links_of_system)
50
+
51
+ after_count = len(self.all_links_of_system)
52
+ print(f"After extending: {after_count} links")
53
+ print(f"Added {after_count - before_count} links (duplicates)")
54
+
55
+ # Return results
56
+ return {
57
+ "all_links_of_system": self.all_links_of_system,
58
+ "content": result["content"]
59
+ }
60
+
61
+ def analyze_topic_with_fix(self, query):
62
+ # Run the strategy
63
+ result = self.strategy.analyze_topic(query)
64
+
65
+ # FIX: Only extend if they're different objects
66
+ print("\nFIX DEMO: Only extending if lists are different")
67
+ before_count = len(self.all_links_of_system)
68
+ print(f"Before fix check: {before_count} links")
69
+
70
+ if id(self.all_links_of_system) != id(self.strategy.all_links_of_system):
71
+ print("Lists are different objects - extending")
72
+ self.all_links_of_system.extend(self.strategy.all_links_of_system)
73
+ else:
74
+ print("Lists are the same object - not extending (avoiding duplicates)")
75
+
76
+ after_count = len(self.all_links_of_system)
77
+ print(f"After fix check: {after_count} links")
78
+ print(f"Added {after_count - before_count} links")
79
+
80
+ # Return results
81
+ return {
82
+ "all_links_of_system": self.all_links_of_system,
83
+ "content": result["content"]
84
+ }
85
+
86
+ def test_bug_and_fix():
87
+ print("=== Testing Bug and Fix ===\n")
88
+
89
+ print("1. Creating first search system to demonstrate the bug:")
90
+ search_system1 = AdvancedSearchSystem()
91
+ result1 = search_system1.analyze_topic_with_bug("What is quantum computing?")
92
+ print(f"\nFinal link count with bug: {len(result1['all_links_of_system'])}")
93
+
94
+ print("\n2. Creating second search system to demonstrate the fix:")
95
+ search_system2 = AdvancedSearchSystem()
96
+ result2 = search_system2.analyze_topic_with_fix("What is quantum computing?")
97
+ print(f"\nFinal link count with fix: {len(result2['all_links_of_system'])}")
98
+
99
+ print("\nTest complete!")
100
+
101
+ if __name__ == "__main__":
102
+ test_bug_and_fix()
@@ -1 +0,0 @@
1
- __version__ = "0.3.6"