local-deep-research 0.3.12__py3-none-any.whl → 0.4.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/__version__.py +1 -1
- local_deep_research/advanced_search_system/filters/base_filter.py +2 -3
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +4 -5
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +298 -0
- local_deep_research/advanced_search_system/findings/repository.py +0 -3
- local_deep_research/advanced_search_system/strategies/base_strategy.py +1 -2
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +14 -18
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +4 -8
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +5 -6
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +2 -2
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +9 -7
- local_deep_research/api/benchmark_functions.py +288 -0
- local_deep_research/api/research_functions.py +8 -4
- local_deep_research/benchmarks/README.md +162 -0
- local_deep_research/benchmarks/__init__.py +51 -0
- local_deep_research/benchmarks/benchmark_functions.py +353 -0
- local_deep_research/benchmarks/cli/__init__.py +16 -0
- local_deep_research/benchmarks/cli/benchmark_commands.py +338 -0
- local_deep_research/benchmarks/cli.py +347 -0
- local_deep_research/benchmarks/comparison/__init__.py +12 -0
- local_deep_research/benchmarks/comparison/evaluator.py +768 -0
- local_deep_research/benchmarks/datasets/__init__.py +53 -0
- local_deep_research/benchmarks/datasets/base.py +295 -0
- local_deep_research/benchmarks/datasets/browsecomp.py +116 -0
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +98 -0
- local_deep_research/benchmarks/datasets/simpleqa.py +74 -0
- local_deep_research/benchmarks/datasets/utils.py +116 -0
- local_deep_research/benchmarks/datasets.py +31 -0
- local_deep_research/benchmarks/efficiency/__init__.py +14 -0
- local_deep_research/benchmarks/efficiency/resource_monitor.py +367 -0
- local_deep_research/benchmarks/efficiency/speed_profiler.py +214 -0
- local_deep_research/benchmarks/evaluators/__init__.py +18 -0
- local_deep_research/benchmarks/evaluators/base.py +74 -0
- local_deep_research/benchmarks/evaluators/browsecomp.py +83 -0
- local_deep_research/benchmarks/evaluators/composite.py +121 -0
- local_deep_research/benchmarks/evaluators/simpleqa.py +271 -0
- local_deep_research/benchmarks/graders.py +410 -0
- local_deep_research/benchmarks/metrics/README.md +80 -0
- local_deep_research/benchmarks/metrics/__init__.py +24 -0
- local_deep_research/benchmarks/metrics/calculation.py +385 -0
- local_deep_research/benchmarks/metrics/reporting.py +155 -0
- local_deep_research/benchmarks/metrics/visualization.py +205 -0
- local_deep_research/benchmarks/metrics.py +11 -0
- local_deep_research/benchmarks/optimization/__init__.py +32 -0
- local_deep_research/benchmarks/optimization/api.py +274 -0
- local_deep_research/benchmarks/optimization/metrics.py +20 -0
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +1163 -0
- local_deep_research/benchmarks/runners.py +434 -0
- local_deep_research/benchmarks/templates.py +65 -0
- local_deep_research/config/llm_config.py +26 -23
- local_deep_research/config/search_config.py +1 -5
- local_deep_research/defaults/default_settings.json +108 -7
- local_deep_research/search_system.py +16 -8
- local_deep_research/utilities/db_utils.py +3 -6
- local_deep_research/utilities/es_utils.py +441 -0
- local_deep_research/utilities/log_utils.py +36 -0
- local_deep_research/utilities/search_utilities.py +8 -9
- local_deep_research/web/app.py +7 -9
- local_deep_research/web/app_factory.py +9 -12
- local_deep_research/web/database/migrations.py +8 -5
- local_deep_research/web/database/models.py +20 -0
- local_deep_research/web/database/schema_upgrade.py +5 -8
- local_deep_research/web/models/database.py +15 -18
- local_deep_research/web/routes/benchmark_routes.py +427 -0
- local_deep_research/web/routes/research_routes.py +13 -17
- local_deep_research/web/routes/settings_routes.py +264 -67
- local_deep_research/web/services/research_service.py +47 -57
- local_deep_research/web/services/settings_manager.py +1 -4
- local_deep_research/web/services/settings_service.py +4 -6
- local_deep_research/web/static/css/styles.css +12 -0
- local_deep_research/web/static/js/components/logpanel.js +164 -155
- local_deep_research/web/static/js/components/research.js +44 -3
- local_deep_research/web/static/js/components/settings.js +27 -0
- local_deep_research/web/static/js/services/socket.js +47 -0
- local_deep_research/web_search_engines/default_search_engines.py +38 -0
- local_deep_research/web_search_engines/engines/meta_search_engine.py +100 -33
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +31 -17
- local_deep_research/web_search_engines/engines/search_engine_brave.py +8 -3
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +343 -0
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +14 -6
- local_deep_research/web_search_engines/engines/search_engine_local.py +19 -23
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +9 -12
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +12 -17
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +8 -4
- local_deep_research/web_search_engines/search_engine_base.py +22 -5
- local_deep_research/web_search_engines/search_engine_factory.py +32 -11
- local_deep_research/web_search_engines/search_engines_config.py +14 -1
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.0.dist-info}/METADATA +10 -2
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.0.dist-info}/RECORD +92 -49
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.0.dist-info}/WHEEL +0 -0
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.0.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
Utilities for logging.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import inspect
|
6
|
+
import logging
|
7
|
+
|
8
|
+
from loguru import logger
|
9
|
+
|
10
|
+
|
11
|
+
class InterceptHandler(logging.Handler):
|
12
|
+
"""
|
13
|
+
Intercepts logging messages and forwards them to Loguru's logger.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def emit(self, record: logging.LogRecord) -> None:
|
17
|
+
# Get corresponding Loguru level if it exists.
|
18
|
+
try:
|
19
|
+
level: str | int = logger.level(record.levelname).name
|
20
|
+
except ValueError:
|
21
|
+
level = record.levelno
|
22
|
+
|
23
|
+
# Find caller from where originated the logged message.
|
24
|
+
frame, depth = inspect.currentframe(), 0
|
25
|
+
while frame:
|
26
|
+
filename = frame.f_code.co_filename
|
27
|
+
is_logging = filename == logging.__file__
|
28
|
+
is_frozen = "importlib" in filename and "_bootstrap" in filename
|
29
|
+
if depth > 0 and not (is_logging or is_frozen):
|
30
|
+
break
|
31
|
+
frame = frame.f_back
|
32
|
+
depth += 1
|
33
|
+
|
34
|
+
logger.opt(depth=depth, exception=record.exc_info).log(
|
35
|
+
level, record.getMessage()
|
36
|
+
)
|
@@ -1,8 +1,7 @@
|
|
1
|
-
import logging
|
2
1
|
import re
|
3
2
|
from typing import Dict, List
|
4
3
|
|
5
|
-
|
4
|
+
from loguru import logger
|
6
5
|
|
7
6
|
|
8
7
|
def remove_think_tags(text: str) -> str:
|
@@ -36,9 +35,9 @@ def extract_links_from_search_results(search_results: List[Dict]) -> List[Dict]:
|
|
36
35
|
|
37
36
|
if title and url:
|
38
37
|
links.append({"title": title, "url": url, "index": index})
|
39
|
-
except Exception
|
38
|
+
except Exception:
|
40
39
|
# Log the specific error for debugging
|
41
|
-
logger.
|
40
|
+
logger.exception("Error extracting link from result")
|
42
41
|
continue
|
43
42
|
return links
|
44
43
|
|
@@ -111,8 +110,8 @@ def format_findings(
|
|
111
110
|
try:
|
112
111
|
links = extract_links_from_search_results(search_results)
|
113
112
|
all_links.extend(links)
|
114
|
-
except Exception
|
115
|
-
logger.
|
113
|
+
except Exception:
|
114
|
+
logger.exception("Error processing search results/links")
|
116
115
|
|
117
116
|
# Start with the synthesized content (passed as synthesized_content)
|
118
117
|
formatted_text += f"{synthesized_content}\n\n"
|
@@ -215,9 +214,9 @@ def format_findings(
|
|
215
214
|
if links:
|
216
215
|
formatted_text += "### SOURCES USED IN THIS SECTION:\n"
|
217
216
|
formatted_text += format_links_to_markdown(links) + "\n\n"
|
218
|
-
except Exception
|
219
|
-
logger.
|
220
|
-
f"Error processing search results/links for finding {idx}
|
217
|
+
except Exception:
|
218
|
+
logger.exception(
|
219
|
+
f"Error processing search results/links for finding {idx}"
|
221
220
|
)
|
222
221
|
else:
|
223
222
|
logger.debug(f"No search_results found for finding item {idx}.")
|
local_deep_research/web/app.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
import logging
|
2
1
|
import os
|
3
2
|
import sys
|
4
3
|
|
4
|
+
from loguru import logger
|
5
|
+
|
5
6
|
from ..setup_data_dir import setup_data_dir
|
6
7
|
from ..utilities.db_utils import get_db_setting
|
7
8
|
from .app_factory import create_app
|
@@ -11,9 +12,6 @@ from .models.database import (
|
|
11
12
|
LEGACY_RESEARCH_HISTORY_DB,
|
12
13
|
)
|
13
14
|
|
14
|
-
# Initialize logger
|
15
|
-
logger = logging.getLogger(__name__)
|
16
|
-
|
17
15
|
# Ensure data directory exists
|
18
16
|
setup_data_dir()
|
19
17
|
|
@@ -24,8 +22,8 @@ if os.path.exists(DB_PATH):
|
|
24
22
|
from .database.schema_upgrade import run_schema_upgrades
|
25
23
|
|
26
24
|
run_schema_upgrades()
|
27
|
-
except Exception
|
28
|
-
logger.
|
25
|
+
except Exception:
|
26
|
+
logger.exception("Error running schema upgrades")
|
29
27
|
logger.warning("Continuing without schema upgrades")
|
30
28
|
|
31
29
|
|
@@ -51,6 +49,7 @@ def check_migration_needed():
|
|
51
49
|
app, socketio = create_app()
|
52
50
|
|
53
51
|
|
52
|
+
@logger.catch()
|
54
53
|
def main():
|
55
54
|
"""
|
56
55
|
Entry point for the web application when run as a command.
|
@@ -85,9 +84,8 @@ def main():
|
|
85
84
|
logger.info("Database migration completed successfully.")
|
86
85
|
else:
|
87
86
|
logger.warning("Database migration failed.")
|
88
|
-
except Exception
|
89
|
-
logger.
|
90
|
-
print(f"Error: {e}")
|
87
|
+
except Exception:
|
88
|
+
logger.exception("Error running database migration")
|
91
89
|
print("Please run migration manually.")
|
92
90
|
|
93
91
|
# Get web server settings with defaults
|
@@ -13,12 +13,11 @@ from flask import (
|
|
13
13
|
)
|
14
14
|
from flask_socketio import SocketIO
|
15
15
|
from flask_wtf.csrf import CSRFProtect
|
16
|
+
from loguru import logger
|
16
17
|
|
18
|
+
from ..utilities.log_utils import InterceptHandler
|
17
19
|
from .models.database import DB_PATH, init_db
|
18
20
|
|
19
|
-
# Initialize logger
|
20
|
-
logger = logging.getLogger(__name__)
|
21
|
-
|
22
21
|
|
23
22
|
def create_app():
|
24
23
|
"""
|
@@ -27,11 +26,9 @@ def create_app():
|
|
27
26
|
Returns:
|
28
27
|
tuple: (app, socketio) - The configured Flask app and SocketIO instance
|
29
28
|
"""
|
30
|
-
# Configure logging
|
31
|
-
logging.basicConfig(level=logging.INFO)
|
32
|
-
|
33
29
|
# Set Werkzeug logger to WARNING level to suppress Socket.IO polling logs
|
34
30
|
logging.getLogger("werkzeug").setLevel(logging.WARNING)
|
31
|
+
logging.getLogger("werkzeug").addHandler(InterceptHandler())
|
35
32
|
|
36
33
|
try:
|
37
34
|
# Get directories based on package installation
|
@@ -42,11 +39,11 @@ def create_app():
|
|
42
39
|
|
43
40
|
# Initialize Flask app with package directories
|
44
41
|
app = Flask(__name__, static_folder=STATIC_DIR, template_folder=TEMPLATE_DIR)
|
45
|
-
|
46
|
-
|
47
|
-
except Exception
|
42
|
+
logger.debug(f"Using package static path: {STATIC_DIR}")
|
43
|
+
logger.debug(f"Using package template path: {TEMPLATE_DIR}")
|
44
|
+
except Exception:
|
48
45
|
# Fallback for development
|
49
|
-
|
46
|
+
logger.exception("Package directories not found, using fallback paths")
|
50
47
|
app = Flask(
|
51
48
|
__name__,
|
52
49
|
static_folder=os.path.abspath("static"),
|
@@ -142,8 +139,8 @@ def apply_middleware(app):
|
|
142
139
|
try:
|
143
140
|
if not request.environ.get("werkzeug.socket"):
|
144
141
|
return
|
145
|
-
except Exception
|
146
|
-
|
142
|
+
except Exception:
|
143
|
+
logger.exception("WebSocket preprocessing error")
|
147
144
|
# Return empty response to prevent further processing
|
148
145
|
return "", 200
|
149
146
|
|
@@ -1,11 +1,8 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
from loguru import logger
|
3
2
|
from sqlalchemy import inspect
|
4
3
|
|
5
4
|
from ..services.settings_manager import SettingsManager
|
6
|
-
from .models import Base, Setting
|
7
|
-
|
8
|
-
logger = logging.getLogger(__name__)
|
5
|
+
from .models import Base, Journal, Setting
|
9
6
|
|
10
7
|
|
11
8
|
def import_default_settings_file(db_session):
|
@@ -27,12 +24,14 @@ def import_default_settings_file(db_session):
|
|
27
24
|
settings_mgr.load_from_defaults_file(overwrite=False, delete_extra=True)
|
28
25
|
logger.info("Successfully imported settings from files")
|
29
26
|
|
27
|
+
|
30
28
|
# Update the saved version.
|
31
29
|
settings_mgr.update_db_version()
|
32
30
|
except Exception as e:
|
33
31
|
logger.error("Error importing settings from files: %s", e)
|
34
32
|
|
35
33
|
|
34
|
+
|
36
35
|
def run_migrations(engine, db_session=None):
|
37
36
|
"""
|
38
37
|
Run any necessary database migrations
|
@@ -47,6 +46,10 @@ def run_migrations(engine, db_session=None):
|
|
47
46
|
logger.info("Creating settings table")
|
48
47
|
Base.metadata.create_all(engine, tables=[Setting.__table__])
|
49
48
|
|
49
|
+
if not inspector.has_table(Journal.__tablename__):
|
50
|
+
logger.info("Creating journals table.")
|
51
|
+
Base.metadata.create_all(engine, tables=[Journal.__table__])
|
52
|
+
|
50
53
|
# Import existing settings from files
|
51
54
|
if db_session:
|
52
55
|
import_default_settings_file(db_session)
|
@@ -9,6 +9,7 @@ from sqlalchemy import (
|
|
9
9
|
Float,
|
10
10
|
ForeignKey,
|
11
11
|
Integer,
|
12
|
+
Sequence,
|
12
13
|
String,
|
13
14
|
Text,
|
14
15
|
UniqueConstraint,
|
@@ -115,3 +116,22 @@ class Setting(Base):
|
|
115
116
|
)
|
116
117
|
|
117
118
|
__table_args__ = (UniqueConstraint("key", name="uix_settings_key"),)
|
119
|
+
|
120
|
+
|
121
|
+
class Journal(Base):
|
122
|
+
"""
|
123
|
+
Database model for storing information about academic journals.
|
124
|
+
"""
|
125
|
+
|
126
|
+
__tablename__ = "journals"
|
127
|
+
|
128
|
+
id = Column(Integer, Sequence("journal_id_seq"), primary_key=True, index=True)
|
129
|
+
|
130
|
+
# Name of the journal
|
131
|
+
name = Column(String(255), nullable=False, unique=True, index=True)
|
132
|
+
# Quality score of the journal
|
133
|
+
quality = Column(Integer, nullable=True)
|
134
|
+
# Model that was used to generate the quality score.
|
135
|
+
quality_model = Column(String(255), nullable=True, index=True)
|
136
|
+
# Time at which the quality was last analyzed.
|
137
|
+
quality_analysis_time = Column(Integer, nullable=False)
|
@@ -3,14 +3,11 @@ Schema upgrade script for Local Deep Research database.
|
|
3
3
|
Handles schema upgrades for existing ldr.db databases.
|
4
4
|
"""
|
5
5
|
|
6
|
-
import logging
|
7
6
|
import os
|
8
7
|
import sqlite3
|
9
8
|
import sys
|
10
9
|
|
11
|
-
|
12
|
-
logging.basicConfig(level=logging.INFO)
|
13
|
-
logger = logging.getLogger(__name__)
|
10
|
+
from loguru import logger
|
14
11
|
|
15
12
|
# Add the parent directory to sys.path to allow relative imports
|
16
13
|
sys.path.append(
|
@@ -67,8 +64,8 @@ def remove_research_log_table(conn):
|
|
67
64
|
else:
|
68
65
|
logger.info("Table 'research_log' does not exist, no action needed")
|
69
66
|
return True
|
70
|
-
except Exception
|
71
|
-
logger.
|
67
|
+
except Exception:
|
68
|
+
logger.exception("Error removing research_log table")
|
72
69
|
return False
|
73
70
|
|
74
71
|
|
@@ -98,8 +95,8 @@ def run_schema_upgrades():
|
|
98
95
|
|
99
96
|
logger.info("Schema upgrades completed successfully")
|
100
97
|
return True
|
101
|
-
except Exception
|
102
|
-
logger.
|
98
|
+
except Exception:
|
99
|
+
logger.exception("Error during schema upgrades")
|
103
100
|
return False
|
104
101
|
|
105
102
|
|
@@ -1,12 +1,9 @@
|
|
1
1
|
import json
|
2
|
-
import logging
|
3
2
|
import os
|
4
3
|
import sqlite3
|
5
|
-
import traceback
|
6
4
|
from datetime import datetime
|
7
5
|
|
8
|
-
|
9
|
-
logger = logging.getLogger(__name__)
|
6
|
+
from loguru import logger
|
10
7
|
|
11
8
|
# Database path
|
12
9
|
# Use unified database in data directory
|
@@ -159,14 +156,14 @@ def calculate_duration(created_at_str, completed_at_str=None):
|
|
159
156
|
end_time = datetime.fromisoformat(
|
160
157
|
completed_at_str.replace(" ", "T")
|
161
158
|
)
|
162
|
-
except Exception
|
163
|
-
|
159
|
+
except Exception:
|
160
|
+
logger.exception("Error parsing completed_at timestamp")
|
164
161
|
try:
|
165
162
|
from dateutil import parser
|
166
163
|
|
167
164
|
end_time = parser.parse(completed_at_str)
|
168
165
|
except Exception:
|
169
|
-
|
166
|
+
logger.exception(
|
170
167
|
f"Fallback parsing also failed for completed_at: {completed_at_str}"
|
171
168
|
)
|
172
169
|
# Fall back to current time
|
@@ -192,23 +189,25 @@ def calculate_duration(created_at_str, completed_at_str=None):
|
|
192
189
|
start_time = datetime.fromisoformat(
|
193
190
|
created_at_str.replace(" ", "T")
|
194
191
|
)
|
195
|
-
except Exception
|
196
|
-
|
192
|
+
except Exception:
|
193
|
+
logger.exception("Error parsing created_at timestamp")
|
197
194
|
# Fallback method if parsing fails
|
198
195
|
try:
|
199
196
|
from dateutil import parser
|
200
197
|
|
201
198
|
start_time = parser.parse(created_at_str)
|
202
199
|
except Exception:
|
203
|
-
|
200
|
+
logger.exception(
|
201
|
+
f"Fallback parsing also failed for created_at:" f" {created_at_str}"
|
202
|
+
)
|
204
203
|
return None
|
205
204
|
|
206
205
|
# Calculate duration if both timestamps are valid
|
207
206
|
if start_time and end_time:
|
208
207
|
try:
|
209
208
|
return int((end_time - start_time).total_seconds())
|
210
|
-
except Exception
|
211
|
-
|
209
|
+
except Exception:
|
210
|
+
logger.exception("Error calculating duration")
|
212
211
|
|
213
212
|
return None
|
214
213
|
|
@@ -238,9 +237,8 @@ def add_log_to_db(research_id, message, log_type="info", progress=None, metadata
|
|
238
237
|
conn.commit()
|
239
238
|
conn.close()
|
240
239
|
return True
|
241
|
-
except Exception
|
242
|
-
|
243
|
-
print(traceback.format_exc())
|
240
|
+
except Exception:
|
241
|
+
logger.exception("Error adding log to database")
|
244
242
|
return False
|
245
243
|
|
246
244
|
|
@@ -288,7 +286,6 @@ def get_logs_for_research(research_id):
|
|
288
286
|
logs.append(formatted_entry)
|
289
287
|
|
290
288
|
return logs
|
291
|
-
except Exception
|
292
|
-
|
293
|
-
print(traceback.format_exc())
|
289
|
+
except Exception:
|
290
|
+
logger.exception("Error retrieving logs from database")
|
294
291
|
return []
|