local-deep-research 0.4.4__py3-none-any.whl → 0.5.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 +7 -0
- local_deep_research/__version__.py +1 -1
- local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
- local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
- local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
- local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
- local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
- local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
- local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
- local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
- local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
- local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
- local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
- local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
- local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
- local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
- local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
- local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
- local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
- local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
- local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
- local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
- local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
- local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
- local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
- local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
- local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
- local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
- local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
- local_deep_research/advanced_search_system/findings/repository.py +54 -17
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
- local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
- local_deep_research/advanced_search_system/questions/__init__.py +16 -0
- local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
- local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
- local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
- local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
- local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
- local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
- local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
- local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
- local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
- local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
- local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
- local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
- local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
- local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
- local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
- local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
- local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
- local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
- local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
- local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
- local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
- local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
- local_deep_research/api/benchmark_functions.py +6 -2
- local_deep_research/api/research_functions.py +10 -4
- local_deep_research/benchmarks/__init__.py +9 -7
- local_deep_research/benchmarks/benchmark_functions.py +6 -2
- local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
- local_deep_research/benchmarks/cli.py +38 -13
- local_deep_research/benchmarks/comparison/__init__.py +4 -2
- local_deep_research/benchmarks/comparison/evaluator.py +316 -239
- local_deep_research/benchmarks/datasets/__init__.py +1 -1
- local_deep_research/benchmarks/datasets/base.py +91 -72
- local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
- local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
- local_deep_research/benchmarks/datasets/utils.py +48 -29
- local_deep_research/benchmarks/datasets.py +4 -11
- local_deep_research/benchmarks/efficiency/__init__.py +8 -4
- local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
- local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
- local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
- local_deep_research/benchmarks/evaluators/composite.py +6 -2
- local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
- local_deep_research/benchmarks/graders.py +32 -10
- local_deep_research/benchmarks/metrics/README.md +1 -1
- local_deep_research/benchmarks/metrics/calculation.py +25 -10
- local_deep_research/benchmarks/metrics/reporting.py +7 -3
- local_deep_research/benchmarks/metrics/visualization.py +42 -23
- local_deep_research/benchmarks/metrics.py +1 -1
- local_deep_research/benchmarks/optimization/__init__.py +3 -1
- local_deep_research/benchmarks/optimization/api.py +7 -1
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
- local_deep_research/benchmarks/runners.py +48 -15
- local_deep_research/citation_handler.py +65 -92
- local_deep_research/citation_handlers/__init__.py +15 -0
- local_deep_research/citation_handlers/base_citation_handler.py +70 -0
- local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
- local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
- local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
- local_deep_research/config/llm_config.py +271 -169
- local_deep_research/config/search_config.py +14 -5
- local_deep_research/defaults/__init__.py +0 -1
- local_deep_research/metrics/__init__.py +13 -0
- local_deep_research/metrics/database.py +58 -0
- local_deep_research/metrics/db_models.py +115 -0
- local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
- local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
- local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
- local_deep_research/metrics/migrate_research_ratings.py +31 -0
- local_deep_research/metrics/models.py +61 -0
- local_deep_research/metrics/pricing/__init__.py +12 -0
- local_deep_research/metrics/pricing/cost_calculator.py +237 -0
- local_deep_research/metrics/pricing/pricing_cache.py +143 -0
- local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
- local_deep_research/metrics/query_utils.py +51 -0
- local_deep_research/metrics/search_tracker.py +380 -0
- local_deep_research/metrics/token_counter.py +1078 -0
- local_deep_research/migrate_db.py +3 -1
- local_deep_research/report_generator.py +22 -8
- local_deep_research/search_system.py +390 -9
- local_deep_research/test_migration.py +15 -5
- local_deep_research/utilities/db_utils.py +7 -4
- local_deep_research/utilities/es_utils.py +115 -104
- local_deep_research/utilities/llm_utils.py +15 -5
- local_deep_research/utilities/log_utils.py +151 -0
- local_deep_research/utilities/search_cache.py +387 -0
- local_deep_research/utilities/search_utilities.py +14 -6
- local_deep_research/utilities/threading_utils.py +92 -0
- local_deep_research/utilities/url_utils.py +6 -0
- local_deep_research/web/api.py +347 -0
- local_deep_research/web/app.py +13 -17
- local_deep_research/web/app_factory.py +71 -66
- local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
- local_deep_research/web/database/migrations.py +5 -3
- local_deep_research/web/database/models.py +51 -2
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +51 -61
- local_deep_research/web/routes/api_routes.py +56 -22
- local_deep_research/web/routes/benchmark_routes.py +4 -1
- local_deep_research/web/routes/globals.py +22 -0
- local_deep_research/web/routes/history_routes.py +71 -46
- local_deep_research/web/routes/metrics_routes.py +1155 -0
- local_deep_research/web/routes/research_routes.py +227 -41
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +310 -103
- local_deep_research/web/services/resource_service.py +36 -11
- local_deep_research/web/services/settings_manager.py +55 -17
- local_deep_research/web/services/settings_service.py +12 -4
- local_deep_research/web/services/socket_service.py +295 -188
- local_deep_research/web/static/css/custom_dropdown.css +180 -0
- local_deep_research/web/static/css/styles.css +39 -1
- local_deep_research/web/static/js/components/detail.js +633 -267
- local_deep_research/web/static/js/components/details.js +751 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
- local_deep_research/web/static/js/components/fallback/ui.js +23 -23
- local_deep_research/web/static/js/components/history.js +76 -76
- local_deep_research/web/static/js/components/logpanel.js +61 -13
- local_deep_research/web/static/js/components/progress.js +13 -2
- local_deep_research/web/static/js/components/research.js +99 -12
- local_deep_research/web/static/js/components/results.js +239 -106
- local_deep_research/web/static/js/main.js +40 -40
- local_deep_research/web/static/js/services/audio.js +1 -1
- local_deep_research/web/static/js/services/formatting.js +11 -11
- local_deep_research/web/static/js/services/keyboard.js +157 -0
- local_deep_research/web/static/js/services/pdf.js +80 -80
- local_deep_research/web/static/sounds/README.md +1 -1
- local_deep_research/web/templates/base.html +1 -0
- local_deep_research/web/templates/components/log_panel.html +7 -1
- local_deep_research/web/templates/components/mobile_nav.html +1 -1
- local_deep_research/web/templates/components/sidebar.html +3 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
- local_deep_research/web/templates/pages/details.html +325 -24
- local_deep_research/web/templates/pages/history.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +1929 -0
- local_deep_research/web/templates/pages/progress.html +2 -2
- local_deep_research/web/templates/pages/research.html +53 -17
- local_deep_research/web/templates/pages/results.html +12 -1
- local_deep_research/web/templates/pages/star_reviews.html +803 -0
- local_deep_research/web/utils/formatters.py +9 -3
- local_deep_research/web_search_engines/default_search_engines.py +5 -3
- local_deep_research/web_search_engines/engines/full_search.py +8 -2
- local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
- local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
- local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
- local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
- local_deep_research/web_search_engines/search_engine_base.py +83 -35
- local_deep_research/web_search_engines/search_engine_factory.py +25 -8
- local_deep_research/web_search_engines/search_engines_config.py +9 -3
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/METADATA +7 -1
- local_deep_research-0.5.0.dist-info/RECORD +265 -0
- local_deep_research-0.4.4.dist-info/RECORD +0 -177
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -15,33 +15,21 @@ from flask import (
|
|
15
15
|
)
|
16
16
|
from loguru import logger
|
17
17
|
|
18
|
-
from ..models.database import
|
19
|
-
from ..services.research_service import
|
18
|
+
from ..models.database import calculate_duration, get_db_connection
|
19
|
+
from ..services.research_service import (
|
20
|
+
run_research_process,
|
21
|
+
start_research_process,
|
22
|
+
)
|
20
23
|
from ..utils.templates import render_template_with_defaults
|
24
|
+
from .globals import active_research, termination_flags
|
21
25
|
|
22
26
|
# Create a Blueprint for the research application
|
23
27
|
research_bp = Blueprint("research", __name__, url_prefix="/research")
|
24
28
|
|
25
|
-
# Active research processes and socket subscriptions
|
26
|
-
active_research = {}
|
27
|
-
socket_subscriptions = {}
|
28
|
-
|
29
|
-
# Add termination flags dictionary
|
30
|
-
termination_flags = {}
|
31
|
-
|
32
29
|
# Output directory for research results
|
33
30
|
OUTPUT_DIR = "research_outputs"
|
34
31
|
|
35
32
|
|
36
|
-
# Return reference to globals for other modules to access
|
37
|
-
def get_globals():
|
38
|
-
return {
|
39
|
-
"active_research": active_research,
|
40
|
-
"socket_subscriptions": socket_subscriptions,
|
41
|
-
"termination_flags": termination_flags,
|
42
|
-
}
|
43
|
-
|
44
|
-
|
45
33
|
# Route for index page - redirection
|
46
34
|
@research_bp.route("/")
|
47
35
|
def index():
|
@@ -141,12 +129,15 @@ def start_research():
|
|
141
129
|
iterations = data.get("iterations")
|
142
130
|
questions_per_iteration = data.get("questions_per_iteration")
|
143
131
|
|
132
|
+
# Add strategy parameter with default value
|
133
|
+
strategy = data.get("strategy", "source-based")
|
134
|
+
|
144
135
|
# Log the selections for troubleshooting
|
145
136
|
logger.info(
|
146
137
|
f"Starting research with provider: {model_provider}, model: {model}, search engine: {search_engine}"
|
147
138
|
)
|
148
139
|
logger.info(
|
149
|
-
f"Additional parameters: max_results={max_results}, time_period={time_period}, iterations={iterations}, questions={questions_per_iteration}"
|
140
|
+
f"Additional parameters: max_results={max_results}, time_period={time_period}, iterations={iterations}, questions={questions_per_iteration}, strategy={strategy}"
|
150
141
|
)
|
151
142
|
|
152
143
|
if not query:
|
@@ -176,7 +167,8 @@ def start_research():
|
|
176
167
|
conn = get_db_connection()
|
177
168
|
cursor = conn.cursor()
|
178
169
|
cursor.execute(
|
179
|
-
"SELECT status FROM research_history WHERE id = ?",
|
170
|
+
"SELECT status FROM research_history WHERE id = ?",
|
171
|
+
(research_id,),
|
180
172
|
)
|
181
173
|
result = cursor.fetchone()
|
182
174
|
conn.close()
|
@@ -235,9 +227,7 @@ def start_research():
|
|
235
227
|
mode,
|
236
228
|
"in_progress",
|
237
229
|
created_at,
|
238
|
-
json.dumps(
|
239
|
-
[{"time": created_at, "message": "Research started", "progress": 0}]
|
240
|
-
),
|
230
|
+
json.dumps([{"time": created_at, "progress": 0}]),
|
241
231
|
json.dumps(research_settings),
|
242
232
|
),
|
243
233
|
)
|
@@ -261,6 +251,7 @@ def start_research():
|
|
261
251
|
time_period=time_period,
|
262
252
|
iterations=iterations,
|
263
253
|
questions_per_iteration=questions_per_iteration,
|
254
|
+
strategy=strategy,
|
264
255
|
)
|
265
256
|
|
266
257
|
# Store the thread reference in active_research
|
@@ -276,12 +267,16 @@ def terminate_research(research_id):
|
|
276
267
|
# Check if the research exists and is in progress
|
277
268
|
conn = get_db_connection()
|
278
269
|
cursor = conn.cursor()
|
279
|
-
cursor.execute(
|
270
|
+
cursor.execute(
|
271
|
+
"SELECT status FROM research_history WHERE id = ?", (research_id,)
|
272
|
+
)
|
280
273
|
result = cursor.fetchone()
|
281
274
|
|
282
275
|
if not result:
|
283
276
|
conn.close()
|
284
|
-
return jsonify(
|
277
|
+
return jsonify(
|
278
|
+
{"status": "error", "message": "Research not found"}
|
279
|
+
), 404
|
285
280
|
|
286
281
|
status = result[0]
|
287
282
|
|
@@ -289,7 +284,9 @@ def terminate_research(research_id):
|
|
289
284
|
if status != "in_progress":
|
290
285
|
conn.close()
|
291
286
|
return (
|
292
|
-
jsonify(
|
287
|
+
jsonify(
|
288
|
+
{"status": "error", "message": "Research is not in progress"}
|
289
|
+
),
|
293
290
|
400,
|
294
291
|
)
|
295
292
|
|
@@ -324,13 +321,7 @@ def terminate_research(research_id):
|
|
324
321
|
active_research[research_id]["log"].append(log_entry)
|
325
322
|
|
326
323
|
# Add to database log
|
327
|
-
|
328
|
-
research_id,
|
329
|
-
termination_message,
|
330
|
-
log_type="milestone",
|
331
|
-
progress=current_progress,
|
332
|
-
metadata={"phase": "termination"},
|
333
|
-
)
|
324
|
+
logger.log("milestone", "Research ended: {}", termination_message)
|
334
325
|
|
335
326
|
# Update the log in the database (old way for backward compatibility)
|
336
327
|
cursor.execute(
|
@@ -370,7 +361,9 @@ def terminate_research(research_id):
|
|
370
361
|
except Exception:
|
371
362
|
logger.exception("Socket emit error (non-critical)")
|
372
363
|
|
373
|
-
return jsonify(
|
364
|
+
return jsonify(
|
365
|
+
{"status": "success", "message": "Research termination requested"}
|
366
|
+
)
|
374
367
|
|
375
368
|
|
376
369
|
@research_bp.route("/api/delete/<int:research_id>", methods=["DELETE"])
|
@@ -381,13 +374,16 @@ def delete_research(research_id):
|
|
381
374
|
|
382
375
|
# First check if the research exists and is not in progress
|
383
376
|
cursor.execute(
|
384
|
-
"SELECT status, report_path FROM research_history WHERE id = ?",
|
377
|
+
"SELECT status, report_path FROM research_history WHERE id = ?",
|
378
|
+
(research_id,),
|
385
379
|
)
|
386
380
|
result = cursor.fetchone()
|
387
381
|
|
388
382
|
if not result:
|
389
383
|
conn.close()
|
390
|
-
return jsonify(
|
384
|
+
return jsonify(
|
385
|
+
{"status": "error", "message": "Research not found"}
|
386
|
+
), 404
|
391
387
|
|
392
388
|
status, report_path = result
|
393
389
|
|
@@ -477,7 +473,9 @@ def open_file_location():
|
|
477
473
|
|
478
474
|
# Check if path exists
|
479
475
|
if not os.path.exists(file_path):
|
480
|
-
return jsonify(
|
476
|
+
return jsonify(
|
477
|
+
{"status": "error", "message": "Path does not exist"}
|
478
|
+
), 404
|
481
479
|
|
482
480
|
try:
|
483
481
|
if platform.system() == "Windows":
|
@@ -506,13 +504,17 @@ def save_raw_config():
|
|
506
504
|
|
507
505
|
if not raw_config:
|
508
506
|
return (
|
509
|
-
jsonify(
|
507
|
+
jsonify(
|
508
|
+
{"success": False, "error": "Raw configuration is required"}
|
509
|
+
),
|
510
510
|
400,
|
511
511
|
)
|
512
512
|
|
513
513
|
try:
|
514
514
|
# Get the config file path
|
515
|
-
config_dir = os.path.join(
|
515
|
+
config_dir = os.path.join(
|
516
|
+
os.path.expanduser("~"), ".local_deep_research"
|
517
|
+
)
|
516
518
|
os.makedirs(config_dir, exist_ok=True)
|
517
519
|
config_path = os.path.join(config_dir, "config.toml")
|
518
520
|
|
@@ -577,7 +579,9 @@ def get_history():
|
|
577
579
|
duration_seconds = None
|
578
580
|
if completed_at and created_at:
|
579
581
|
try:
|
580
|
-
duration_seconds = calculate_duration(
|
582
|
+
duration_seconds = calculate_duration(
|
583
|
+
created_at, completed_at
|
584
|
+
)
|
581
585
|
except Exception:
|
582
586
|
logger.exception("Error calculating duration")
|
583
587
|
|
@@ -606,6 +610,187 @@ def get_history():
|
|
606
610
|
return jsonify({"status": "error", "message": str(e)}), 500
|
607
611
|
|
608
612
|
|
613
|
+
@research_bp.route("/api/research/<int:research_id>")
|
614
|
+
def get_research_details(research_id):
|
615
|
+
"""Get full details of a research"""
|
616
|
+
try:
|
617
|
+
conn = get_db_connection()
|
618
|
+
cursor = conn.cursor()
|
619
|
+
cursor.execute(
|
620
|
+
"""SELECT id, query, status, progress, mode,
|
621
|
+
created_at, completed_at, report_path, metadata
|
622
|
+
FROM research_history WHERE id = ?""",
|
623
|
+
(research_id,),
|
624
|
+
)
|
625
|
+
result = cursor.fetchone()
|
626
|
+
|
627
|
+
if result is None:
|
628
|
+
conn.close()
|
629
|
+
return jsonify({"error": "Research not found"}), 404
|
630
|
+
|
631
|
+
# Unpack the result
|
632
|
+
(
|
633
|
+
research_id,
|
634
|
+
query,
|
635
|
+
status,
|
636
|
+
progress,
|
637
|
+
mode,
|
638
|
+
created_at,
|
639
|
+
completed_at,
|
640
|
+
report_path,
|
641
|
+
metadata_str,
|
642
|
+
) = result
|
643
|
+
|
644
|
+
# Parse metadata if it exists
|
645
|
+
metadata = {}
|
646
|
+
if metadata_str:
|
647
|
+
try:
|
648
|
+
metadata = json.loads(metadata_str)
|
649
|
+
except json.JSONDecodeError:
|
650
|
+
logger.warning(
|
651
|
+
f"Invalid JSON in metadata for research {research_id}"
|
652
|
+
)
|
653
|
+
|
654
|
+
conn.close()
|
655
|
+
|
656
|
+
return jsonify(
|
657
|
+
{
|
658
|
+
"id": research_id,
|
659
|
+
"query": query,
|
660
|
+
"status": status,
|
661
|
+
"progress": progress,
|
662
|
+
"progress_percentage": progress or 0,
|
663
|
+
"mode": mode,
|
664
|
+
"created_at": created_at,
|
665
|
+
"completed_at": completed_at,
|
666
|
+
"report_path": report_path,
|
667
|
+
"metadata": metadata,
|
668
|
+
}
|
669
|
+
)
|
670
|
+
except Exception as e:
|
671
|
+
logger.exception(f"Error getting research details: {str(e)}")
|
672
|
+
return jsonify({"error": "An internal error has occurred"}), 500
|
673
|
+
|
674
|
+
|
675
|
+
@research_bp.route("/api/research/<int:research_id>/logs")
|
676
|
+
def get_research_logs(research_id):
|
677
|
+
"""Get logs for a specific research"""
|
678
|
+
try:
|
679
|
+
conn = get_db_connection()
|
680
|
+
cursor = conn.cursor()
|
681
|
+
|
682
|
+
# First check if the research exists
|
683
|
+
cursor.execute(
|
684
|
+
"SELECT id FROM research_history WHERE id = ?", (research_id,)
|
685
|
+
)
|
686
|
+
if cursor.fetchone() is None:
|
687
|
+
conn.close()
|
688
|
+
return jsonify({"error": "Research not found"}), 404
|
689
|
+
|
690
|
+
# Get logs from research_logs table
|
691
|
+
cursor.execute(
|
692
|
+
"""SELECT id, message, timestamp, log_type, progress, metadata
|
693
|
+
FROM research_logs
|
694
|
+
WHERE research_id = ?
|
695
|
+
ORDER BY timestamp ASC""",
|
696
|
+
(research_id,),
|
697
|
+
)
|
698
|
+
|
699
|
+
logs = []
|
700
|
+
for row in cursor.fetchall():
|
701
|
+
log_id, message, timestamp, log_type, progress, metadata_str = row
|
702
|
+
|
703
|
+
# Parse metadata if it exists
|
704
|
+
metadata = {}
|
705
|
+
if metadata_str:
|
706
|
+
try:
|
707
|
+
metadata = json.loads(metadata_str)
|
708
|
+
except json.JSONDecodeError:
|
709
|
+
pass
|
710
|
+
|
711
|
+
logs.append(
|
712
|
+
{
|
713
|
+
"id": log_id,
|
714
|
+
"message": message,
|
715
|
+
"timestamp": timestamp,
|
716
|
+
"log_type": log_type,
|
717
|
+
"progress": progress,
|
718
|
+
"metadata": metadata,
|
719
|
+
}
|
720
|
+
)
|
721
|
+
|
722
|
+
conn.close()
|
723
|
+
return jsonify(logs)
|
724
|
+
|
725
|
+
except Exception as e:
|
726
|
+
logger.exception(f"Error getting research logs: {str(e)}")
|
727
|
+
return jsonify({"error": "An internal error has occurred"}), 500
|
728
|
+
|
729
|
+
|
730
|
+
@research_bp.route("/api/report/<int:research_id>")
|
731
|
+
def get_research_report(research_id):
|
732
|
+
"""Get the research report content"""
|
733
|
+
from ..database.models import Research
|
734
|
+
from ...utilities.db_utils import get_db_session
|
735
|
+
|
736
|
+
session = get_db_session()
|
737
|
+
try:
|
738
|
+
# Query using ORM
|
739
|
+
research = session.query(Research).filter_by(id=research_id).first()
|
740
|
+
|
741
|
+
if research is None:
|
742
|
+
return jsonify({"error": "Research not found"}), 404
|
743
|
+
|
744
|
+
# Parse metadata if it exists
|
745
|
+
metadata = {}
|
746
|
+
if research.metadata:
|
747
|
+
try:
|
748
|
+
metadata = json.loads(research.metadata)
|
749
|
+
except json.JSONDecodeError:
|
750
|
+
logger.warning(
|
751
|
+
f"Invalid JSON in metadata for research {research_id}"
|
752
|
+
)
|
753
|
+
|
754
|
+
# Check if report file exists
|
755
|
+
if not research.report_path or not os.path.exists(research.report_path):
|
756
|
+
return jsonify({"error": "Report file not found"}), 404
|
757
|
+
|
758
|
+
# Read the report content
|
759
|
+
try:
|
760
|
+
with open(research.report_path, "r", encoding="utf-8") as f:
|
761
|
+
content = f.read()
|
762
|
+
except Exception as e:
|
763
|
+
logger.error(
|
764
|
+
f"Error reading report file {research.report_path}: {e}"
|
765
|
+
)
|
766
|
+
return jsonify({"error": "Error reading report file"}), 500
|
767
|
+
|
768
|
+
# Return the report data
|
769
|
+
return jsonify(
|
770
|
+
{
|
771
|
+
"content": content,
|
772
|
+
"metadata": {
|
773
|
+
"query": research.query,
|
774
|
+
"mode": research.mode.value if research.mode else None,
|
775
|
+
"created_at": research.created_at.isoformat()
|
776
|
+
if research.created_at
|
777
|
+
else None,
|
778
|
+
"completed_at": research.completed_at.isoformat()
|
779
|
+
if research.completed_at
|
780
|
+
else None,
|
781
|
+
"report_path": research.report_path,
|
782
|
+
**metadata,
|
783
|
+
},
|
784
|
+
}
|
785
|
+
)
|
786
|
+
|
787
|
+
except Exception as e:
|
788
|
+
logger.exception(f"Error getting research report: {str(e)}")
|
789
|
+
return jsonify({"error": "An internal error has occurred"}), 500
|
790
|
+
finally:
|
791
|
+
session.close()
|
792
|
+
|
793
|
+
|
609
794
|
@research_bp.route("/api/research/<research_id>/status")
|
610
795
|
def get_research_status(research_id):
|
611
796
|
"""Get the status of a research process"""
|
@@ -648,7 +833,8 @@ def get_research_status(research_id):
|
|
648
833
|
"suggestion": "Try again later or use a smaller query scope.",
|
649
834
|
}
|
650
835
|
elif (
|
651
|
-
"token limit" in error_msg.lower()
|
836
|
+
"token limit" in error_msg.lower()
|
837
|
+
or "context length" in error_msg.lower()
|
652
838
|
):
|
653
839
|
error_type = "token_limit"
|
654
840
|
error_info = {
|