local-deep-research 0.3.12__py3-none-any.whl → 0.4.1__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 +1 -0
- 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 +15 -10
- 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 +58 -73
- 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 +30 -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.1.dist-info}/METADATA +10 -2
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.1.dist-info}/RECORD +93 -51
- local_deep_research/app.py +0 -8
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.1.dist-info}/WHEEL +0 -0
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.1.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.3.12.dist-info → local_deep_research-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,10 @@
|
|
1
|
+
import hashlib
|
1
2
|
import json
|
2
|
-
import logging
|
3
|
-
import os
|
4
3
|
import threading
|
5
|
-
import traceback
|
6
4
|
from datetime import datetime
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from loguru import logger
|
7
8
|
|
8
9
|
from ...config.llm_config import get_llm
|
9
10
|
from ...config.search_config import get_search
|
@@ -13,11 +14,8 @@ from ...utilities.search_utilities import extract_links_from_search_results
|
|
13
14
|
from ..models.database import add_log_to_db, calculate_duration, get_db_connection
|
14
15
|
from .socket_service import emit_to_subscribers
|
15
16
|
|
16
|
-
# Initialize logger
|
17
|
-
logger = logging.getLogger(__name__)
|
18
|
-
|
19
17
|
# Output directory for research results
|
20
|
-
OUTPUT_DIR = "research_outputs"
|
18
|
+
OUTPUT_DIR = Path("research_outputs")
|
21
19
|
|
22
20
|
|
23
21
|
def start_research_process(
|
@@ -70,6 +68,25 @@ def start_research_process(
|
|
70
68
|
return thread
|
71
69
|
|
72
70
|
|
71
|
+
def _generate_report_path(query: str) -> Path:
|
72
|
+
"""
|
73
|
+
Generates a path for a new report file based on the query.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
query: The query used for the report.
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
The path that it generated.
|
80
|
+
|
81
|
+
"""
|
82
|
+
# Generate a unique filename that does not contain
|
83
|
+
# non-alphanumeric characters.
|
84
|
+
query_hash = hashlib.md5(query.encode("utf-8")).hexdigest()[:10]
|
85
|
+
return OUTPUT_DIR / (
|
86
|
+
f"research_report_{query_hash}_{int(datetime.now().timestamp())}.md"
|
87
|
+
)
|
88
|
+
|
89
|
+
|
73
90
|
def run_research_process(
|
74
91
|
research_id, query, mode, active_research, termination_flags, **kwargs
|
75
92
|
):
|
@@ -121,8 +138,8 @@ def run_research_process(
|
|
121
138
|
)
|
122
139
|
|
123
140
|
# Set up the AI Context Manager
|
124
|
-
output_dir =
|
125
|
-
|
141
|
+
output_dir = OUTPUT_DIR / f"research_{research_id}"
|
142
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
126
143
|
|
127
144
|
# Set up progress callback
|
128
145
|
def progress_callback(message, progress_percent, metadata):
|
@@ -239,8 +256,8 @@ def run_research_process(
|
|
239
256
|
event_data["log_entry"] = log_entry
|
240
257
|
|
241
258
|
emit_to_subscribers("research_progress", research_id, event_data)
|
242
|
-
except Exception
|
243
|
-
logger.
|
259
|
+
except Exception:
|
260
|
+
logger.exception("Socket emit error (non-critical)")
|
244
261
|
|
245
262
|
# Function to check termination during long-running operations
|
246
263
|
def check_termination():
|
@@ -275,14 +292,12 @@ def run_research_process(
|
|
275
292
|
model_provider,
|
276
293
|
model,
|
277
294
|
)
|
278
|
-
except Exception
|
279
|
-
logger.
|
280
|
-
"Error setting LLM provider=%s, model=%s
|
295
|
+
except Exception:
|
296
|
+
logger.exception(
|
297
|
+
"Error setting LLM provider=%s, model=%s",
|
281
298
|
model_provider,
|
282
299
|
model,
|
283
|
-
str(e),
|
284
300
|
)
|
285
|
-
logger.error(traceback.format_exc())
|
286
301
|
|
287
302
|
# Set the progress callback in the system
|
288
303
|
system = AdvancedSearchSystem(llm=use_llm)
|
@@ -302,10 +317,8 @@ def run_research_process(
|
|
302
317
|
)
|
303
318
|
|
304
319
|
logger.info("Successfully set search engine to: %s", search_engine)
|
305
|
-
except Exception
|
306
|
-
logger.
|
307
|
-
"Error setting search engine to %s: %s", search_engine, str(e)
|
308
|
-
)
|
320
|
+
except Exception:
|
321
|
+
logger.exception("Error setting search engine to %s", search_engine)
|
309
322
|
|
310
323
|
# Run the search
|
311
324
|
progress_callback("Starting research process", 5, {"phase": "init"})
|
@@ -358,10 +371,8 @@ def run_research_process(
|
|
358
371
|
if isinstance(
|
359
372
|
raw_formatted_findings, str
|
360
373
|
) and raw_formatted_findings.startswith("Error:"):
|
361
|
-
|
362
|
-
|
363
|
-
logger.warning(
|
364
|
-
f"Detected error in formatted findings: {raw_formatted_findings[:100]}... stack trace: {traceback.format_exc()}"
|
374
|
+
logger.exception(
|
375
|
+
f"Detected error in formatted findings: {raw_formatted_findings[:100]}..."
|
365
376
|
)
|
366
377
|
|
367
378
|
# Determine error type for better user feedback
|
@@ -502,9 +513,9 @@ def run_research_process(
|
|
502
513
|
search_results
|
503
514
|
)
|
504
515
|
all_links.extend(links)
|
505
|
-
except Exception
|
506
|
-
logger.
|
507
|
-
|
516
|
+
except Exception:
|
517
|
+
logger.exception(
|
518
|
+
"Error processing search results/links"
|
508
519
|
)
|
509
520
|
|
510
521
|
logger.info(
|
@@ -520,18 +531,8 @@ def run_research_process(
|
|
520
531
|
)
|
521
532
|
|
522
533
|
# Save as markdown file
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
safe_query = "".join(
|
527
|
-
x for x in query if x.isalnum() or x in [" ", "-", "_"]
|
528
|
-
)[:50]
|
529
|
-
safe_query = safe_query.replace(" ", "_").lower()
|
530
|
-
report_path = os.path.join(
|
531
|
-
OUTPUT_DIR,
|
532
|
-
f"quick_summary_{safe_query}_"
|
533
|
-
f"{int(datetime.now().timestamp())}.md",
|
534
|
-
)
|
534
|
+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
535
|
+
report_path = _generate_report_path(query)
|
535
536
|
|
536
537
|
# Send progress update for writing to file
|
537
538
|
progress_callback(
|
@@ -541,7 +542,7 @@ def run_research_process(
|
|
541
542
|
)
|
542
543
|
|
543
544
|
logger.info("Writing report to: %s", report_path)
|
544
|
-
with open(
|
545
|
+
with report_path.open("w", encoding="utf-8") as f:
|
545
546
|
f.write("# Quick Research Summary\n\n")
|
546
547
|
f.write(f"Query: {query}\n\n")
|
547
548
|
f.write(clean_markdown)
|
@@ -579,7 +580,7 @@ def run_research_process(
|
|
579
580
|
"completed",
|
580
581
|
completed_at,
|
581
582
|
duration_seconds,
|
582
|
-
report_path,
|
583
|
+
str(report_path),
|
583
584
|
json.dumps(metadata),
|
584
585
|
research_id,
|
585
586
|
),
|
@@ -594,7 +595,7 @@ def run_research_process(
|
|
594
595
|
progress_callback(
|
595
596
|
"Research completed successfully",
|
596
597
|
100,
|
597
|
-
{"phase": "complete", "report_path": report_path},
|
598
|
+
{"phase": "complete", "report_path": str(report_path)},
|
598
599
|
)
|
599
600
|
|
600
601
|
# Clean up resources
|
@@ -607,10 +608,7 @@ def run_research_process(
|
|
607
608
|
logger.info("Resources cleaned up for research_id: %s", research_id)
|
608
609
|
|
609
610
|
except Exception as inner_e:
|
610
|
-
logger.
|
611
|
-
"Error during quick summary generation: %s", str(inner_e)
|
612
|
-
)
|
613
|
-
logger.error(traceback.format_exc())
|
611
|
+
logger.exception("Error during quick summary generation")
|
614
612
|
raise Exception(f"Error generating quick summary: {str(inner_e)}")
|
615
613
|
else:
|
616
614
|
raise Exception(
|
@@ -634,19 +632,10 @@ def run_research_process(
|
|
634
632
|
)
|
635
633
|
|
636
634
|
# Save as markdown file
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
safe_query = "".join(
|
641
|
-
x for x in query if x.isalnum() or x in [" ", "-", "_"]
|
642
|
-
)[:50]
|
643
|
-
safe_query = safe_query.replace(" ", "_").lower()
|
644
|
-
report_path = os.path.join(
|
645
|
-
OUTPUT_DIR,
|
646
|
-
f"detailed_report_{safe_query}_{int(datetime.now().timestamp())}.md",
|
647
|
-
)
|
635
|
+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
636
|
+
report_path = _generate_report_path(query)
|
648
637
|
|
649
|
-
with open(
|
638
|
+
with report_path.open("w", encoding="utf-8") as f:
|
650
639
|
f.write(final_report["content"])
|
651
640
|
|
652
641
|
# Update database
|
@@ -674,7 +663,7 @@ def run_research_process(
|
|
674
663
|
"completed",
|
675
664
|
completed_at,
|
676
665
|
duration_seconds,
|
677
|
-
report_path,
|
666
|
+
str(report_path),
|
678
667
|
json.dumps(metadata),
|
679
668
|
research_id,
|
680
669
|
),
|
@@ -685,7 +674,7 @@ def run_research_process(
|
|
685
674
|
progress_callback(
|
686
675
|
"Research completed successfully",
|
687
676
|
100,
|
688
|
-
{"phase": "complete", "report_path": report_path},
|
677
|
+
{"phase": "complete", "report_path": str(report_path)},
|
689
678
|
)
|
690
679
|
|
691
680
|
# Clean up resources
|
@@ -694,10 +683,7 @@ def run_research_process(
|
|
694
683
|
except Exception as e:
|
695
684
|
# Handle error
|
696
685
|
error_message = f"Research failed: {str(e)}"
|
697
|
-
logger.
|
698
|
-
import traceback
|
699
|
-
|
700
|
-
logger.error("Exception occurred:" + str(traceback.print_exc()))
|
686
|
+
logger.exception(error_message)
|
701
687
|
|
702
688
|
try:
|
703
689
|
# Check for common Ollama error patterns in the exception and provide more user-friendly errors
|
@@ -783,12 +769,11 @@ def run_research_process(
|
|
783
769
|
research_id,
|
784
770
|
{"status": status, "error": message},
|
785
771
|
)
|
786
|
-
except Exception
|
787
|
-
logger.
|
772
|
+
except Exception:
|
773
|
+
logger.exception("Failed to emit error via socket")
|
788
774
|
|
789
|
-
except Exception
|
790
|
-
logger.
|
791
|
-
logger.error(traceback.format_exc())
|
775
|
+
except Exception:
|
776
|
+
logger.exception("Error in error handler")
|
792
777
|
|
793
778
|
# Clean up resources
|
794
779
|
cleanup_research_resources(research_id, active_research, termination_flags)
|
@@ -817,8 +802,8 @@ def cleanup_research_resources(research_id, active_research, termination_flags):
|
|
817
802
|
if result and result[0]:
|
818
803
|
current_status = result[0]
|
819
804
|
conn.close()
|
820
|
-
except Exception
|
821
|
-
logger.
|
805
|
+
except Exception:
|
806
|
+
logger.exception("Error retrieving research status during cleanup")
|
822
807
|
|
823
808
|
# Remove from active research
|
824
809
|
if research_id in active_research:
|
@@ -860,8 +845,8 @@ def cleanup_research_resources(research_id, active_research, termination_flags):
|
|
860
845
|
|
861
846
|
emit_to_subscribers("research_progress", research_id, final_message)
|
862
847
|
|
863
|
-
except Exception
|
864
|
-
logger.error("Error sending final cleanup message
|
848
|
+
except Exception:
|
849
|
+
logger.error("Error sending final cleanup message")
|
865
850
|
|
866
851
|
|
867
852
|
def handle_termination(research_id, active_research, termination_flags):
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import importlib.resources as pkg_resources
|
2
2
|
import json
|
3
|
-
import logging
|
4
3
|
import os
|
5
4
|
from typing import Any, Dict, Optional, Union
|
6
5
|
|
6
|
+
from loguru import logger
|
7
7
|
from sqlalchemy import func
|
8
8
|
from sqlalchemy.exc import SQLAlchemyError
|
9
9
|
from sqlalchemy.orm import Session
|
@@ -19,9 +19,6 @@ from ..models.settings import (
|
|
19
19
|
SearchSetting,
|
20
20
|
)
|
21
21
|
|
22
|
-
# Setup logging
|
23
|
-
logger = logging.getLogger(__name__)
|
24
|
-
|
25
22
|
|
26
23
|
def check_env_setting(key: str) -> str | None:
|
27
24
|
"""
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import logging
|
2
1
|
from typing import Any, Dict, Optional, Union
|
3
2
|
|
3
|
+
from loguru import logger
|
4
|
+
|
4
5
|
from ..database.models import Setting
|
5
6
|
from .settings_manager import SettingsManager
|
6
7
|
|
7
|
-
# Initialize logger
|
8
|
-
logger = logging.getLogger(__name__)
|
9
|
-
|
10
8
|
|
11
9
|
def get_settings_manager(db_session=None):
|
12
10
|
"""
|
@@ -110,8 +108,8 @@ def bulk_update_settings(
|
|
110
108
|
if commit and success and manager.db_session:
|
111
109
|
try:
|
112
110
|
manager.db_session.commit()
|
113
|
-
except Exception
|
114
|
-
logger.
|
111
|
+
except Exception:
|
112
|
+
logger.exception("Error committing bulk settings update")
|
115
113
|
manager.db_session.rollback()
|
116
114
|
success = False
|
117
115
|
|
@@ -1383,6 +1383,18 @@ textarea:focus, input[type="text"]:focus {
|
|
1383
1383
|
border-color: rgba(255, 193, 7, 0.2);
|
1384
1384
|
}
|
1385
1385
|
|
1386
|
+
/* Styling for search engine selection log entries */
|
1387
|
+
.console-log-entry[data-log-type="info"][data-engine-selected="true"] {
|
1388
|
+
background-color: rgba(64, 191, 255, 0.05);
|
1389
|
+
border-left: 2px solid var(--accent-tertiary);
|
1390
|
+
padding-left: calc(0.5rem - 2px);
|
1391
|
+
font-weight: 500;
|
1392
|
+
}
|
1393
|
+
|
1394
|
+
.console-log-entry[data-log-type="info"][data-engine-selected="true"] .log-message {
|
1395
|
+
color: var(--accent-tertiary);
|
1396
|
+
}
|
1397
|
+
|
1386
1398
|
/* Update existing error message styling if needed */
|
1387
1399
|
.error-message {
|
1388
1400
|
display: none;
|