local-deep-research 0.4.4__py3-none-any.whl → 0.5.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.
- 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 +20 -3
- local_deep_research/web/database/models.py +74 -25
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +63 -83
- 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 +192 -54
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +412 -251
- 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.2.dist-info}/METADATA +7 -1
- local_deep_research-0.5.2.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.2.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -59,9 +59,7 @@ def api_start_research():
|
|
59
59
|
mode,
|
60
60
|
"in_progress",
|
61
61
|
created_at,
|
62
|
-
json.dumps(
|
63
|
-
[{"time": created_at, "message": "Research started", "progress": 0}]
|
64
|
-
),
|
62
|
+
json.dumps([{"time": created_at, "progress": 0}]),
|
65
63
|
json.dumps(research_settings),
|
66
64
|
),
|
67
65
|
)
|
@@ -89,9 +87,11 @@ def api_start_research():
|
|
89
87
|
"research_id": research_id,
|
90
88
|
}
|
91
89
|
)
|
92
|
-
except Exception
|
93
|
-
logger.
|
94
|
-
return jsonify(
|
90
|
+
except Exception:
|
91
|
+
logger.exception("Error starting research")
|
92
|
+
return jsonify(
|
93
|
+
{"status": "error", "message": "Failed to start research"}, 500
|
94
|
+
)
|
95
95
|
|
96
96
|
|
97
97
|
@api_bp.route("/status/<int:research_id>", methods=["GET"])
|
@@ -120,7 +120,9 @@ def api_research_status(research_id):
|
|
120
120
|
try:
|
121
121
|
metadata = json.loads(metadata_str)
|
122
122
|
except json.JSONDecodeError:
|
123
|
-
logger.warning(
|
123
|
+
logger.warning(
|
124
|
+
f"Invalid JSON in metadata for research {research_id}"
|
125
|
+
)
|
124
126
|
|
125
127
|
return jsonify(
|
126
128
|
{
|
@@ -144,10 +146,18 @@ def api_terminate_research(research_id):
|
|
144
146
|
try:
|
145
147
|
result = cancel_research(research_id)
|
146
148
|
return jsonify(
|
147
|
-
{
|
149
|
+
{
|
150
|
+
"status": "success",
|
151
|
+
"message": "Research terminated",
|
152
|
+
"result": result,
|
153
|
+
}
|
154
|
+
)
|
155
|
+
except Exception:
|
156
|
+
logger.exception("Error terminating research")
|
157
|
+
return (
|
158
|
+
jsonify({"status": "error", "message": "Failed to stop research."}),
|
159
|
+
500,
|
148
160
|
)
|
149
|
-
except Exception as e:
|
150
|
-
return jsonify({"status": "error", "message": str(e)}), 500
|
151
161
|
|
152
162
|
|
153
163
|
@api_bp.route("/resources/<int:research_id>", methods=["GET"])
|
@@ -158,8 +168,11 @@ def api_get_resources(research_id):
|
|
158
168
|
try:
|
159
169
|
resources = get_resources_for_research(research_id)
|
160
170
|
return jsonify({"status": "success", "resources": resources})
|
161
|
-
except Exception
|
162
|
-
|
171
|
+
except Exception:
|
172
|
+
logger.exception("Error getting resources for research")
|
173
|
+
return jsonify(
|
174
|
+
{"status": "error", "message": "Failed to get resources"}, 500
|
175
|
+
)
|
163
176
|
|
164
177
|
|
165
178
|
@api_bp.route("/resources/<int:research_id>", methods=["POST"])
|
@@ -182,19 +195,25 @@ def api_add_resource(research_id):
|
|
182
195
|
# Validate required fields
|
183
196
|
if not title or not url:
|
184
197
|
return (
|
185
|
-
jsonify(
|
198
|
+
jsonify(
|
199
|
+
{"status": "error", "message": "Title and URL are required"}
|
200
|
+
),
|
186
201
|
400,
|
187
202
|
)
|
188
203
|
|
189
204
|
# Check if the research exists
|
190
205
|
conn = get_db_connection()
|
191
206
|
cursor = conn.cursor()
|
192
|
-
cursor.execute(
|
207
|
+
cursor.execute(
|
208
|
+
"SELECT id FROM research_history WHERE id = ?", (research_id,)
|
209
|
+
)
|
193
210
|
result = cursor.fetchone()
|
194
211
|
conn.close()
|
195
212
|
|
196
213
|
if not result:
|
197
|
-
return jsonify(
|
214
|
+
return jsonify(
|
215
|
+
{"status": "error", "message": "Research not found"}
|
216
|
+
), 404
|
198
217
|
|
199
218
|
# Add the resource
|
200
219
|
resource_id = add_resource(
|
@@ -231,10 +250,15 @@ def api_delete_resource(research_id, resource_id):
|
|
231
250
|
|
232
251
|
if success:
|
233
252
|
return jsonify(
|
234
|
-
{
|
253
|
+
{
|
254
|
+
"status": "success",
|
255
|
+
"message": "Resource deleted successfully",
|
256
|
+
}
|
235
257
|
)
|
236
258
|
else:
|
237
|
-
return jsonify(
|
259
|
+
return jsonify(
|
260
|
+
{"status": "error", "message": "Resource not found"}
|
261
|
+
), 404
|
238
262
|
except Exception as e:
|
239
263
|
logger.error(f"Error deleting resource: {str(e)}")
|
240
264
|
return jsonify({"status": "error", "message": str(e)}), 500
|
@@ -252,7 +276,10 @@ def check_ollama_status():
|
|
252
276
|
|
253
277
|
if provider.lower() != "ollama":
|
254
278
|
return jsonify(
|
255
|
-
{
|
279
|
+
{
|
280
|
+
"running": True,
|
281
|
+
"message": f"Using provider: {provider}, not Ollama",
|
282
|
+
}
|
256
283
|
)
|
257
284
|
|
258
285
|
# Get Ollama API URL from LLM config
|
@@ -272,7 +299,9 @@ def check_ollama_status():
|
|
272
299
|
response = requests.get(f"{ollama_base_url}/api/tags", timeout=5)
|
273
300
|
|
274
301
|
# Add response details for debugging
|
275
|
-
logger.debug(
|
302
|
+
logger.debug(
|
303
|
+
f"Ollama status check response code: {response.status_code}"
|
304
|
+
)
|
276
305
|
|
277
306
|
if response.status_code == 200:
|
278
307
|
# Try to validate the response content
|
@@ -420,7 +449,9 @@ def check_ollama_model():
|
|
420
449
|
|
421
450
|
# Debug log the first bit of the response
|
422
451
|
response_preview = (
|
423
|
-
str(data)[:500] + "..."
|
452
|
+
str(data)[:500] + "..."
|
453
|
+
if len(str(data)) > 500
|
454
|
+
else str(data)
|
424
455
|
)
|
425
456
|
logger.debug(f"Ollama API response data: {response_preview}")
|
426
457
|
|
@@ -448,7 +479,8 @@ def check_ollama_model():
|
|
448
479
|
|
449
480
|
# Case-insensitive model name comparison
|
450
481
|
model_exists = any(
|
451
|
-
m.get("name", "").lower() == model_name.lower()
|
482
|
+
m.get("name", "").lower() == model_name.lower()
|
483
|
+
for m in models
|
452
484
|
)
|
453
485
|
|
454
486
|
if model_exists:
|
@@ -561,7 +593,9 @@ def api_get_config():
|
|
561
593
|
"search_tool", "auto"
|
562
594
|
),
|
563
595
|
"features": {
|
564
|
-
"notifications": current_app.config.get(
|
596
|
+
"notifications": current_app.config.get(
|
597
|
+
"ENABLE_NOTIFICATIONS", False
|
598
|
+
)
|
565
599
|
},
|
566
600
|
}
|
567
601
|
|
@@ -190,7 +190,10 @@ def get_benchmark_config():
|
|
190
190
|
],
|
191
191
|
"evaluation_models": {
|
192
192
|
"openai_endpoint": [
|
193
|
-
{
|
193
|
+
{
|
194
|
+
"id": "anthropic/claude-3.7-sonnet",
|
195
|
+
"name": "Claude 3.7 Sonnet",
|
196
|
+
}
|
194
197
|
],
|
195
198
|
"openai": [
|
196
199
|
{"id": "gpt-4o", "name": "GPT-4o"},
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"""
|
2
|
+
Stores global state.
|
3
|
+
"""
|
4
|
+
|
5
|
+
# Active research processes and socket subscriptions
|
6
|
+
active_research = {}
|
7
|
+
socket_subscriptions = {}
|
8
|
+
# Add termination flags dictionary
|
9
|
+
termination_flags = {}
|
10
|
+
|
11
|
+
|
12
|
+
def get_globals():
|
13
|
+
"""
|
14
|
+
Returns:
|
15
|
+
Global state for other modules to access.
|
16
|
+
|
17
|
+
"""
|
18
|
+
return {
|
19
|
+
"active_research": active_research,
|
20
|
+
"socket_subscriptions": socket_subscriptions,
|
21
|
+
"termination_flags": termination_flags,
|
22
|
+
}
|
@@ -1,10 +1,17 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
3
|
import traceback
|
4
|
+
from pathlib import Path
|
4
5
|
|
5
6
|
from flask import Blueprint, jsonify, make_response
|
6
7
|
|
7
|
-
from ..models.database import
|
8
|
+
from ..models.database import (
|
9
|
+
get_db_connection,
|
10
|
+
get_logs_for_research,
|
11
|
+
get_total_logs_for_research,
|
12
|
+
)
|
13
|
+
from ..routes.globals import get_globals
|
14
|
+
from ..services.research_service import get_research_strategy
|
8
15
|
|
9
16
|
# Initialize logger
|
10
17
|
logger = logging.getLogger(__name__)
|
@@ -13,6 +20,20 @@ logger = logging.getLogger(__name__)
|
|
13
20
|
history_bp = Blueprint("history", __name__)
|
14
21
|
|
15
22
|
|
23
|
+
def resolve_report_path(report_path: str) -> Path:
|
24
|
+
"""
|
25
|
+
Resolve report path to absolute path using pathlib.
|
26
|
+
Handles both absolute and relative paths.
|
27
|
+
"""
|
28
|
+
path = Path(report_path)
|
29
|
+
if path.is_absolute():
|
30
|
+
return path
|
31
|
+
|
32
|
+
# If relative path, make it relative to project root
|
33
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
34
|
+
return project_root / path
|
35
|
+
|
36
|
+
|
16
37
|
@history_bp.route("/history", methods=["GET"])
|
17
38
|
def get_history():
|
18
39
|
"""Get the research history"""
|
@@ -24,7 +45,9 @@ def get_history():
|
|
24
45
|
cursor = conn.cursor()
|
25
46
|
|
26
47
|
# Get all history records ordered by latest first
|
27
|
-
cursor.execute(
|
48
|
+
cursor.execute(
|
49
|
+
"SELECT * FROM research_history ORDER BY created_at DESC"
|
50
|
+
)
|
28
51
|
results = cursor.fetchall()
|
29
52
|
conn.close()
|
30
53
|
|
@@ -135,15 +158,16 @@ def get_research_status(research_id):
|
|
135
158
|
column[0]: row[idx] for idx, column in enumerate(cursor.description)
|
136
159
|
}
|
137
160
|
cursor = conn.cursor()
|
138
|
-
cursor.execute(
|
161
|
+
cursor.execute(
|
162
|
+
"SELECT * FROM research_history WHERE id = ?", (research_id,)
|
163
|
+
)
|
139
164
|
result = cursor.fetchone()
|
140
165
|
conn.close()
|
141
166
|
|
142
167
|
if not result:
|
143
|
-
return jsonify(
|
144
|
-
|
145
|
-
|
146
|
-
from .research_routes import get_globals
|
168
|
+
return jsonify(
|
169
|
+
{"status": "error", "message": "Research not found"}
|
170
|
+
), 404
|
147
171
|
|
148
172
|
globals_dict = get_globals()
|
149
173
|
active_research = globals_dict["active_research"]
|
@@ -176,18 +200,22 @@ def get_research_details(research_id):
|
|
176
200
|
column[0]: row[idx] for idx, column in enumerate(cursor.description)
|
177
201
|
}
|
178
202
|
cursor = conn.cursor()
|
179
|
-
cursor.execute(
|
203
|
+
cursor.execute(
|
204
|
+
"SELECT * FROM research_history WHERE id = ?", (research_id,)
|
205
|
+
)
|
180
206
|
result = cursor.fetchone()
|
181
207
|
conn.close()
|
182
208
|
|
183
209
|
if not result:
|
184
|
-
return jsonify(
|
210
|
+
return jsonify(
|
211
|
+
{"status": "error", "message": "Research not found"}
|
212
|
+
), 404
|
185
213
|
|
186
214
|
# Get logs from the dedicated log database
|
187
215
|
logs = get_logs_for_research(research_id)
|
188
216
|
|
189
|
-
#
|
190
|
-
|
217
|
+
# Get strategy information
|
218
|
+
strategy_name = get_research_strategy(research_id)
|
191
219
|
|
192
220
|
globals_dict = get_globals()
|
193
221
|
active_research = globals_dict["active_research"]
|
@@ -215,6 +243,7 @@ def get_research_details(research_id):
|
|
215
243
|
"query": result.get("query"),
|
216
244
|
"mode": result.get("mode"),
|
217
245
|
"status": result.get("status"),
|
246
|
+
"strategy": strategy_name,
|
218
247
|
"progress": active_research.get(research_id, {}).get(
|
219
248
|
"progress", 100 if result.get("status") == "completed" else 0
|
220
249
|
),
|
@@ -232,7 +261,9 @@ def get_report(research_id):
|
|
232
261
|
column[0]: row[idx] for idx, column in enumerate(cursor.description)
|
233
262
|
}
|
234
263
|
cursor = conn.cursor()
|
235
|
-
cursor.execute(
|
264
|
+
cursor.execute(
|
265
|
+
"SELECT * FROM research_history WHERE id = ?", (research_id,)
|
266
|
+
)
|
236
267
|
result = cursor.fetchone()
|
237
268
|
conn.close()
|
238
269
|
|
@@ -240,7 +271,10 @@ def get_report(research_id):
|
|
240
271
|
return jsonify({"status": "error", "message": "Report not found"}), 404
|
241
272
|
|
242
273
|
try:
|
243
|
-
|
274
|
+
# Resolve report path using helper function
|
275
|
+
report_path = resolve_report_path(result["report_path"])
|
276
|
+
|
277
|
+
with open(report_path, "r", encoding="utf-8") as f:
|
244
278
|
content = f.read()
|
245
279
|
|
246
280
|
# Create an enhanced metadata dictionary with database fields
|
@@ -280,7 +314,9 @@ def get_markdown(research_id):
|
|
280
314
|
column[0]: row[idx] for idx, column in enumerate(cursor.description)
|
281
315
|
}
|
282
316
|
cursor = conn.cursor()
|
283
|
-
cursor.execute(
|
317
|
+
cursor.execute(
|
318
|
+
"SELECT * FROM research_history WHERE id = ?", (research_id,)
|
319
|
+
)
|
284
320
|
result = cursor.fetchone()
|
285
321
|
conn.close()
|
286
322
|
|
@@ -288,7 +324,10 @@ def get_markdown(research_id):
|
|
288
324
|
return jsonify({"status": "error", "message": "Report not found"}), 404
|
289
325
|
|
290
326
|
try:
|
291
|
-
|
327
|
+
# Resolve report path using helper function
|
328
|
+
report_path = resolve_report_path(result["report_path"])
|
329
|
+
|
330
|
+
with open(report_path, "r", encoding="utf-8") as f:
|
292
331
|
content = f.read()
|
293
332
|
return jsonify({"status": "success", "content": content})
|
294
333
|
except Exception as e:
|
@@ -304,12 +343,16 @@ def get_research_logs(research_id):
|
|
304
343
|
column[0]: row[idx] for idx, column in enumerate(cursor.description)
|
305
344
|
}
|
306
345
|
cursor = conn.cursor()
|
307
|
-
cursor.execute(
|
346
|
+
cursor.execute(
|
347
|
+
"SELECT id FROM research_history WHERE id = ?", (research_id,)
|
348
|
+
)
|
308
349
|
result = cursor.fetchone()
|
309
350
|
conn.close()
|
310
351
|
|
311
352
|
if not result:
|
312
|
-
return jsonify(
|
353
|
+
return jsonify(
|
354
|
+
{"status": "error", "message": "Research not found"}
|
355
|
+
), 404
|
313
356
|
|
314
357
|
# Retrieve logs from the database
|
315
358
|
logs = get_logs_for_research(research_id)
|
@@ -317,38 +360,20 @@ def get_research_logs(research_id):
|
|
317
360
|
# Format logs correctly if needed
|
318
361
|
formatted_logs = []
|
319
362
|
for log in logs:
|
363
|
+
log_entry = log.copy()
|
320
364
|
# Ensure each log has time, message, and type fields
|
321
|
-
log_entry =
|
322
|
-
|
323
|
-
|
324
|
-
"type": log.get("type", "info"),
|
325
|
-
}
|
365
|
+
log_entry["time"] = log.get("time", "")
|
366
|
+
log_entry["message"] = log.get("message", "No message")
|
367
|
+
log_entry["type"] = log.get("type", "info")
|
326
368
|
formatted_logs.append(log_entry)
|
327
369
|
|
328
|
-
|
329
|
-
from .research_routes import get_globals
|
330
|
-
|
331
|
-
globals_dict = get_globals()
|
332
|
-
active_research = globals_dict["active_research"]
|
333
|
-
|
334
|
-
# Add any current logs from memory if this is an active research
|
335
|
-
if research_id in active_research and active_research[research_id].get("log"):
|
336
|
-
# Use the logs from memory temporarily until they're saved to the database
|
337
|
-
memory_logs = active_research[research_id]["log"]
|
338
|
-
|
339
|
-
# Format memory logs too
|
340
|
-
for log in memory_logs:
|
341
|
-
log_entry = {
|
342
|
-
"time": log.get("time", ""),
|
343
|
-
"message": log.get("message", "No message"),
|
344
|
-
"type": log.get("type", "info"),
|
345
|
-
}
|
370
|
+
return jsonify({"status": "success", "logs": formatted_logs})
|
346
371
|
|
347
|
-
# Check if this log is already in our formatted logs by timestamp
|
348
|
-
if not any(flog["time"] == log_entry["time"] for flog in formatted_logs):
|
349
|
-
formatted_logs.append(log_entry)
|
350
372
|
|
351
|
-
|
352
|
-
|
373
|
+
@history_bp.route("/log_count/<int:research_id>")
|
374
|
+
def get_log_count(research_id):
|
375
|
+
"""Get the total number of logs for a specific research ID"""
|
376
|
+
# Get the total number of logs for this research ID
|
377
|
+
total_logs = get_total_logs_for_research(research_id)
|
353
378
|
|
354
|
-
return jsonify({"status": "success", "
|
379
|
+
return jsonify({"status": "success", "total_logs": total_logs})
|