local-deep-research 0.1.26__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. local_deep_research/__init__.py +23 -22
  2. local_deep_research/__main__.py +16 -0
  3. local_deep_research/advanced_search_system/__init__.py +7 -0
  4. local_deep_research/advanced_search_system/filters/__init__.py +8 -0
  5. local_deep_research/advanced_search_system/filters/base_filter.py +38 -0
  6. local_deep_research/advanced_search_system/filters/cross_engine_filter.py +200 -0
  7. local_deep_research/advanced_search_system/findings/base_findings.py +81 -0
  8. local_deep_research/advanced_search_system/findings/repository.py +452 -0
  9. local_deep_research/advanced_search_system/knowledge/__init__.py +1 -0
  10. local_deep_research/advanced_search_system/knowledge/base_knowledge.py +151 -0
  11. local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +159 -0
  12. local_deep_research/advanced_search_system/questions/__init__.py +1 -0
  13. local_deep_research/advanced_search_system/questions/base_question.py +64 -0
  14. local_deep_research/advanced_search_system/questions/decomposition_question.py +445 -0
  15. local_deep_research/advanced_search_system/questions/standard_question.py +119 -0
  16. local_deep_research/advanced_search_system/repositories/__init__.py +7 -0
  17. local_deep_research/advanced_search_system/strategies/__init__.py +1 -0
  18. local_deep_research/advanced_search_system/strategies/base_strategy.py +118 -0
  19. local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +450 -0
  20. local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +312 -0
  21. local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +270 -0
  22. local_deep_research/advanced_search_system/strategies/standard_strategy.py +300 -0
  23. local_deep_research/advanced_search_system/tools/__init__.py +1 -0
  24. local_deep_research/advanced_search_system/tools/base_tool.py +100 -0
  25. local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +1 -0
  26. local_deep_research/advanced_search_system/tools/question_tools/__init__.py +1 -0
  27. local_deep_research/advanced_search_system/tools/search_tools/__init__.py +1 -0
  28. local_deep_research/api/__init__.py +5 -5
  29. local_deep_research/api/research_functions.py +154 -160
  30. local_deep_research/app.py +8 -0
  31. local_deep_research/citation_handler.py +25 -16
  32. local_deep_research/{config.py → config/config_files.py} +102 -110
  33. local_deep_research/config/llm_config.py +472 -0
  34. local_deep_research/config/search_config.py +77 -0
  35. local_deep_research/defaults/__init__.py +10 -5
  36. local_deep_research/defaults/main.toml +2 -2
  37. local_deep_research/defaults/search_engines.toml +60 -34
  38. local_deep_research/main.py +121 -19
  39. local_deep_research/migrate_db.py +147 -0
  40. local_deep_research/report_generator.py +87 -45
  41. local_deep_research/search_system.py +153 -283
  42. local_deep_research/setup_data_dir.py +35 -0
  43. local_deep_research/test_migration.py +178 -0
  44. local_deep_research/utilities/__init__.py +0 -0
  45. local_deep_research/utilities/db_utils.py +49 -0
  46. local_deep_research/{utilties → utilities}/enums.py +2 -2
  47. local_deep_research/{utilties → utilities}/llm_utils.py +63 -29
  48. local_deep_research/utilities/search_utilities.py +242 -0
  49. local_deep_research/{utilties → utilities}/setup_utils.py +4 -2
  50. local_deep_research/web/__init__.py +0 -1
  51. local_deep_research/web/app.py +86 -1709
  52. local_deep_research/web/app_factory.py +289 -0
  53. local_deep_research/web/database/README.md +70 -0
  54. local_deep_research/web/database/migrate_to_ldr_db.py +289 -0
  55. local_deep_research/web/database/migrations.py +447 -0
  56. local_deep_research/web/database/models.py +117 -0
  57. local_deep_research/web/database/schema_upgrade.py +107 -0
  58. local_deep_research/web/models/database.py +294 -0
  59. local_deep_research/web/models/settings.py +94 -0
  60. local_deep_research/web/routes/api_routes.py +559 -0
  61. local_deep_research/web/routes/history_routes.py +354 -0
  62. local_deep_research/web/routes/research_routes.py +715 -0
  63. local_deep_research/web/routes/settings_routes.py +1583 -0
  64. local_deep_research/web/services/research_service.py +947 -0
  65. local_deep_research/web/services/resource_service.py +149 -0
  66. local_deep_research/web/services/settings_manager.py +669 -0
  67. local_deep_research/web/services/settings_service.py +187 -0
  68. local_deep_research/web/services/socket_service.py +210 -0
  69. local_deep_research/web/static/css/custom_dropdown.css +277 -0
  70. local_deep_research/web/static/css/settings.css +1223 -0
  71. local_deep_research/web/static/css/styles.css +525 -48
  72. local_deep_research/web/static/js/components/custom_dropdown.js +428 -0
  73. local_deep_research/web/static/js/components/detail.js +348 -0
  74. local_deep_research/web/static/js/components/fallback/formatting.js +122 -0
  75. local_deep_research/web/static/js/components/fallback/ui.js +215 -0
  76. local_deep_research/web/static/js/components/history.js +487 -0
  77. local_deep_research/web/static/js/components/logpanel.js +949 -0
  78. local_deep_research/web/static/js/components/progress.js +1107 -0
  79. local_deep_research/web/static/js/components/research.js +1865 -0
  80. local_deep_research/web/static/js/components/results.js +766 -0
  81. local_deep_research/web/static/js/components/settings.js +3981 -0
  82. local_deep_research/web/static/js/components/settings_sync.js +106 -0
  83. local_deep_research/web/static/js/main.js +226 -0
  84. local_deep_research/web/static/js/services/api.js +253 -0
  85. local_deep_research/web/static/js/services/audio.js +31 -0
  86. local_deep_research/web/static/js/services/formatting.js +119 -0
  87. local_deep_research/web/static/js/services/pdf.js +622 -0
  88. local_deep_research/web/static/js/services/socket.js +882 -0
  89. local_deep_research/web/static/js/services/ui.js +546 -0
  90. local_deep_research/web/templates/base.html +72 -0
  91. local_deep_research/web/templates/components/custom_dropdown.html +47 -0
  92. local_deep_research/web/templates/components/log_panel.html +32 -0
  93. local_deep_research/web/templates/components/mobile_nav.html +22 -0
  94. local_deep_research/web/templates/components/settings_form.html +299 -0
  95. local_deep_research/web/templates/components/sidebar.html +21 -0
  96. local_deep_research/web/templates/pages/details.html +73 -0
  97. local_deep_research/web/templates/pages/history.html +51 -0
  98. local_deep_research/web/templates/pages/progress.html +57 -0
  99. local_deep_research/web/templates/pages/research.html +139 -0
  100. local_deep_research/web/templates/pages/results.html +59 -0
  101. local_deep_research/web/templates/settings_dashboard.html +78 -192
  102. local_deep_research/web/utils/__init__.py +0 -0
  103. local_deep_research/web/utils/formatters.py +76 -0
  104. local_deep_research/web_search_engines/engines/full_search.py +18 -16
  105. local_deep_research/web_search_engines/engines/meta_search_engine.py +182 -131
  106. local_deep_research/web_search_engines/engines/search_engine_arxiv.py +224 -139
  107. local_deep_research/web_search_engines/engines/search_engine_brave.py +88 -71
  108. local_deep_research/web_search_engines/engines/search_engine_ddg.py +48 -39
  109. local_deep_research/web_search_engines/engines/search_engine_github.py +415 -204
  110. local_deep_research/web_search_engines/engines/search_engine_google_pse.py +123 -90
  111. local_deep_research/web_search_engines/engines/search_engine_guardian.py +210 -157
  112. local_deep_research/web_search_engines/engines/search_engine_local.py +532 -369
  113. local_deep_research/web_search_engines/engines/search_engine_local_all.py +42 -36
  114. local_deep_research/web_search_engines/engines/search_engine_pubmed.py +358 -266
  115. local_deep_research/web_search_engines/engines/search_engine_searxng.py +212 -160
  116. local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +213 -170
  117. local_deep_research/web_search_engines/engines/search_engine_serpapi.py +84 -68
  118. local_deep_research/web_search_engines/engines/search_engine_wayback.py +186 -154
  119. local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +115 -77
  120. local_deep_research/web_search_engines/search_engine_base.py +174 -99
  121. local_deep_research/web_search_engines/search_engine_factory.py +192 -102
  122. local_deep_research/web_search_engines/search_engines_config.py +22 -15
  123. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/METADATA +177 -97
  124. local_deep_research-0.2.2.dist-info/RECORD +135 -0
  125. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/WHEEL +1 -2
  126. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/entry_points.txt +3 -0
  127. local_deep_research/defaults/llm_config.py +0 -338
  128. local_deep_research/utilties/search_utilities.py +0 -114
  129. local_deep_research/web/static/js/app.js +0 -3763
  130. local_deep_research/web/templates/api_keys_config.html +0 -82
  131. local_deep_research/web/templates/collections_config.html +0 -90
  132. local_deep_research/web/templates/index.html +0 -348
  133. local_deep_research/web/templates/llm_config.html +0 -120
  134. local_deep_research/web/templates/main_config.html +0 -89
  135. local_deep_research/web/templates/search_engines_config.html +0 -154
  136. local_deep_research/web/templates/settings.html +0 -519
  137. local_deep_research-0.1.26.dist-info/RECORD +0 -61
  138. local_deep_research-0.1.26.dist-info/top_level.txt +0 -1
  139. /local_deep_research/{utilties → config}/__init__.py +0 -0
  140. {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,119 @@
1
+ """
2
+ Standard question generation implementation.
3
+ """
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import List
8
+
9
+ from .base_question import BaseQuestionGenerator
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class StandardQuestionGenerator(BaseQuestionGenerator):
15
+ """Standard question generator."""
16
+
17
+ def generate_questions(
18
+ self,
19
+ current_knowledge: str,
20
+ query: str,
21
+ questions_per_iteration: int = 2,
22
+ questions_by_iteration: dict = None,
23
+ ) -> List[str]:
24
+ """Generate follow-up questions based on current knowledge."""
25
+ now = datetime.now()
26
+ current_time = now.strftime("%Y-%m-%d")
27
+ questions_by_iteration = questions_by_iteration or {}
28
+
29
+ logger.info("Generating follow-up questions...")
30
+
31
+ if questions_by_iteration:
32
+ prompt = f"""Critically reflect current knowledge (e.g., timeliness), what {questions_per_iteration} high-quality internet search questions remain unanswered to exactly answer the query?
33
+ Query: {query}
34
+ Today: {current_time}
35
+ Past questions: {str(questions_by_iteration)}
36
+ Knowledge: {current_knowledge}
37
+ Include questions that critically reflect current knowledge.
38
+ \n\n\nFormat: One question per line, e.g. \n Q: question1 \n Q: question2\n\n"""
39
+ else:
40
+ prompt = f" You will have follow up questions. First, identify if your knowledge is outdated (high chance). Today: {current_time}. Generate {questions_per_iteration} high-quality internet search questions to exactly answer: {query}\n\n\nFormat: One question per line, e.g. \n Q: question1 \n Q: question2\n\n"
41
+
42
+ response = self.model.invoke(prompt)
43
+
44
+ # Handle both string responses and responses with .content attribute
45
+ response_text = ""
46
+ if hasattr(response, "content"):
47
+ response_text = response.content
48
+ else:
49
+ # Handle string responses
50
+ response_text = str(response)
51
+
52
+ questions = [
53
+ q.replace("Q:", "").strip()
54
+ for q in response_text.split("\n")
55
+ if q.strip().startswith("Q:")
56
+ ][:questions_per_iteration]
57
+
58
+ logger.info(f"Generated {len(questions)} follow-up questions")
59
+
60
+ return questions
61
+
62
+ def generate_sub_questions(self, query: str, context: str = "") -> List[str]:
63
+ """
64
+ Generate sub-questions from a main query.
65
+
66
+ Args:
67
+ query: The main query to break down
68
+ context: Additional context for question generation
69
+
70
+ Returns:
71
+ List[str]: List of generated sub-questions
72
+ """
73
+ prompt = f"""You are an expert at breaking down complex questions into simpler sub-questions.
74
+
75
+ Original Question: {query}
76
+
77
+ {context}
78
+
79
+ Break down the original question into 2-5 simpler sub-questions that would help answer the original question when answered in sequence.
80
+ Follow these guidelines:
81
+ 1. Each sub-question should be specific and answerable on its own
82
+ 2. Sub-questions should build towards answering the original question
83
+ 3. For multi-hop or complex queries, identify the individual facts or entities needed
84
+ 4. Ensure the sub-questions can be answered with separate searches
85
+
86
+ Format your response as a numbered list with ONLY the sub-questions, one per line:
87
+ 1. First sub-question
88
+ 2. Second sub-question
89
+ ...
90
+
91
+ Only provide the numbered sub-questions, nothing else."""
92
+
93
+ try:
94
+ response = self.model.invoke(prompt)
95
+
96
+ # Handle both string responses and responses with .content attribute
97
+ content = ""
98
+ if hasattr(response, "content"):
99
+ content = response.content
100
+ else:
101
+ # Handle string responses
102
+ content = str(response)
103
+
104
+ # Parse sub-questions from the response
105
+ sub_questions = []
106
+ for line in content.strip().split("\n"):
107
+ line = line.strip()
108
+ if line and (line[0].isdigit() or line.startswith("-")):
109
+ # Extract sub-question from numbered or bulleted list
110
+ parts = line.split(".", 1) if "." in line else line.split(" ", 1)
111
+ if len(parts) > 1:
112
+ sub_question = parts[1].strip()
113
+ sub_questions.append(sub_question)
114
+
115
+ # Limit to at most 5 sub-questions
116
+ return sub_questions[:5]
117
+ except Exception as e:
118
+ logger.error(f"Error generating sub-questions: {str(e)}")
119
+ return []
@@ -0,0 +1,7 @@
1
+ """
2
+ Repositories package for advanced search system.
3
+ """
4
+
5
+ from ..findings.repository import FindingsRepository
6
+
7
+ __all__ = ["FindingsRepository"]
@@ -0,0 +1 @@
1
+ # Search System Strategies Package
@@ -0,0 +1,118 @@
1
+ """
2
+ Base class for all search strategies.
3
+ Defines the common interface and shared functionality for different search approaches.
4
+ """
5
+
6
+ import logging
7
+ from abc import ABC, abstractmethod
8
+ from typing import Callable, Dict, List, Optional
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class BaseSearchStrategy(ABC):
14
+ """Abstract base class for all search strategies."""
15
+
16
+ def __init__(self):
17
+ """Initialize the base strategy with common attributes."""
18
+ self.progress_callback = None
19
+ self.questions_by_iteration = {}
20
+ self.all_links_of_system = []
21
+
22
+ def set_progress_callback(self, callback: Callable[[str, int, dict], None]) -> None:
23
+ """Set a callback function to receive progress updates."""
24
+ self.progress_callback = callback
25
+
26
+ def _update_progress(
27
+ self,
28
+ message: str,
29
+ progress_percent: Optional[int] = None,
30
+ metadata: Optional[dict] = None,
31
+ ) -> None:
32
+ """Send a progress update via the callback if available."""
33
+ if self.progress_callback:
34
+ self.progress_callback(message, progress_percent, metadata or {})
35
+
36
+ @abstractmethod
37
+ def analyze_topic(self, query: str) -> Dict:
38
+ """
39
+ Analyze a topic using the strategy's specific approach.
40
+
41
+ Args:
42
+ query: The research query to analyze
43
+
44
+ Returns:
45
+ Dict containing:
46
+ - findings: List of research findings
47
+ - iterations: Number of iterations completed
48
+ - questions: Questions generated by iteration
49
+ - formatted_findings: Formatted output
50
+ - current_knowledge: Accumulated knowledge
51
+ - error: Optional error message
52
+ """
53
+ pass
54
+
55
+ def _validate_search_engine(self) -> bool:
56
+ """
57
+ Validate that the search engine is available and configured.
58
+
59
+ Returns:
60
+ bool: True if search engine is available, False otherwise
61
+ """
62
+ if not hasattr(self, "search") or self.search is None:
63
+ error_msg = (
64
+ "Error: No search engine available. Please check your configuration."
65
+ )
66
+ self._update_progress(
67
+ error_msg,
68
+ 100,
69
+ {
70
+ "phase": "error",
71
+ "error": "No search engine available",
72
+ "status": "failed",
73
+ },
74
+ )
75
+ return False
76
+ return True
77
+
78
+ def _handle_search_error(
79
+ self, error: Exception, question: str, progress_base: int
80
+ ) -> List:
81
+ """
82
+ Handle errors during search execution.
83
+
84
+ Args:
85
+ error: The exception that occurred
86
+ question: The question being searched
87
+ progress_base: The current progress percentage
88
+
89
+ Returns:
90
+ List: Empty list to continue processing
91
+ """
92
+ error_msg = f"Error during search: {str(error)}"
93
+ logger.error(f"SEARCH ERROR: {error_msg}")
94
+ self._update_progress(
95
+ error_msg,
96
+ progress_base + 2,
97
+ {"phase": "search_error", "error": str(error)},
98
+ )
99
+ return []
100
+
101
+ def _handle_analysis_error(
102
+ self, error: Exception, question: str, progress_base: int
103
+ ) -> None:
104
+ """
105
+ Handle errors during result analysis.
106
+
107
+ Args:
108
+ error: The exception that occurred
109
+ question: The question being analyzed
110
+ progress_base: The current progress percentage
111
+ """
112
+ error_msg = f"Error analyzing results: {str(error)}"
113
+ logger.info(f"ANALYSIS ERROR: {error_msg}")
114
+ self._update_progress(
115
+ error_msg,
116
+ progress_base + 10,
117
+ {"phase": "analysis_error", "error": str(error)},
118
+ )