local-deep-research 0.1.26__py3-none-any.whl → 0.2.0__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 +96 -84
- 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 +72 -44
- local_deep_research/search_system.py +147 -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 +1592 -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 +211 -159
- 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.0.dist-info}/METADATA +177 -97
- local_deep_research-0.2.0.dist-info/RECORD +135 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.0.dist-info}/WHEEL +1 -2
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.0.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.0.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 @@
|
|
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
|
+
)
|