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
@@ -9,13 +9,14 @@ import logging
|
|
9
9
|
import threading
|
10
10
|
import time
|
11
11
|
from contextlib import contextmanager
|
12
|
-
from typing import
|
12
|
+
from typing import Any, Dict
|
13
13
|
|
14
14
|
logger = logging.getLogger(__name__)
|
15
15
|
|
16
16
|
# Try to import psutil, but don't fail if not available
|
17
17
|
try:
|
18
18
|
import psutil
|
19
|
+
|
19
20
|
PSUTIL_AVAILABLE = True
|
20
21
|
except ImportError:
|
21
22
|
PSUTIL_AVAILABLE = False
|
@@ -25,19 +26,21 @@ except ImportError:
|
|
25
26
|
class ResourceMonitor:
|
26
27
|
"""
|
27
28
|
Monitor system resource usage during research.
|
28
|
-
|
29
|
+
|
29
30
|
This class provides methods for tracking CPU, memory, and disk usage
|
30
31
|
during system execution. It can be used to identify resource bottlenecks
|
31
32
|
and optimize configurations for different hardware environments.
|
32
33
|
"""
|
33
|
-
|
34
|
-
def __init__(
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
sampling_interval: float = 1.0,
|
38
|
+
track_process: bool = True,
|
39
|
+
track_system: bool = True,
|
40
|
+
):
|
38
41
|
"""
|
39
42
|
Initialize the resource monitor.
|
40
|
-
|
43
|
+
|
41
44
|
Args:
|
42
45
|
sampling_interval: Seconds between resource usage measurements
|
43
46
|
track_process: Whether to track this process's resource usage
|
@@ -46,117 +49,128 @@ class ResourceMonitor:
|
|
46
49
|
self.sampling_interval = sampling_interval
|
47
50
|
self.track_process = track_process
|
48
51
|
self.track_system = track_system
|
49
|
-
|
52
|
+
|
50
53
|
self.monitoring = False
|
51
54
|
self.monitor_thread = None
|
52
|
-
|
55
|
+
|
53
56
|
# Resource usage data
|
54
57
|
self.process_data = []
|
55
58
|
self.system_data = []
|
56
59
|
self.start_time = None
|
57
60
|
self.end_time = None
|
58
|
-
|
61
|
+
|
59
62
|
# Check if we can monitor resources
|
60
63
|
self.can_monitor = PSUTIL_AVAILABLE
|
61
64
|
if not self.can_monitor:
|
62
|
-
logger.warning(
|
63
|
-
|
65
|
+
logger.warning(
|
66
|
+
"Resource monitoring requires psutil. Install with: pip install psutil"
|
67
|
+
)
|
68
|
+
|
64
69
|
def start(self):
|
65
70
|
"""Start monitoring resource usage."""
|
66
71
|
if not self.can_monitor:
|
67
|
-
logger.warning(
|
72
|
+
logger.warning(
|
73
|
+
"Resource monitoring not available (psutil not installed)"
|
74
|
+
)
|
68
75
|
return
|
69
|
-
|
76
|
+
|
70
77
|
if self.monitoring:
|
71
78
|
logger.warning("Resource monitoring already started")
|
72
79
|
return
|
73
|
-
|
80
|
+
|
74
81
|
self.process_data = []
|
75
82
|
self.system_data = []
|
76
83
|
self.start_time = time.time()
|
77
84
|
self.monitoring = True
|
78
|
-
|
85
|
+
|
79
86
|
# Start monitoring in a background thread
|
80
87
|
self.monitor_thread = threading.Thread(
|
81
|
-
target=self._monitor_resources,
|
82
|
-
daemon=True
|
88
|
+
target=self._monitor_resources, daemon=True
|
83
89
|
)
|
84
90
|
self.monitor_thread.start()
|
85
|
-
|
86
|
-
logger.info(
|
87
|
-
|
91
|
+
|
92
|
+
logger.info(
|
93
|
+
f"Resource monitoring started with {self.sampling_interval}s interval"
|
94
|
+
)
|
95
|
+
|
88
96
|
def stop(self):
|
89
97
|
"""Stop monitoring resource usage."""
|
90
98
|
if not self.monitoring:
|
91
99
|
return
|
92
|
-
|
100
|
+
|
93
101
|
self.monitoring = False
|
94
102
|
self.end_time = time.time()
|
95
|
-
|
103
|
+
|
96
104
|
# Wait for the monitoring thread to finish
|
97
105
|
if self.monitor_thread:
|
98
106
|
self.monitor_thread.join(timeout=2.0)
|
99
107
|
self.monitor_thread = None
|
100
|
-
|
108
|
+
|
101
109
|
logger.info("Resource monitoring stopped")
|
102
|
-
|
110
|
+
|
103
111
|
def _monitor_resources(self):
|
104
112
|
"""Background thread that collects resource usage data."""
|
105
113
|
if not PSUTIL_AVAILABLE:
|
106
114
|
return
|
107
|
-
|
115
|
+
|
108
116
|
# Get this process
|
109
117
|
current_process = psutil.Process()
|
110
|
-
|
118
|
+
|
111
119
|
while self.monitoring:
|
112
120
|
timestamp = time.time()
|
113
|
-
|
121
|
+
|
114
122
|
try:
|
115
123
|
# Monitor this process
|
116
124
|
if self.track_process:
|
117
125
|
process_cpu = current_process.cpu_percent(interval=None)
|
118
126
|
process_memory = current_process.memory_info()
|
119
|
-
|
120
|
-
self.process_data.append(
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
127
|
+
|
128
|
+
self.process_data.append(
|
129
|
+
{
|
130
|
+
"timestamp": timestamp,
|
131
|
+
"cpu_percent": process_cpu,
|
132
|
+
"memory_rss": process_memory.rss, # Resident Set Size in bytes
|
133
|
+
"memory_vms": process_memory.vms, # Virtual Memory Size in bytes
|
134
|
+
"memory_shared": getattr(
|
135
|
+
process_memory, "shared", 0
|
136
|
+
),
|
137
|
+
"num_threads": current_process.num_threads(),
|
138
|
+
"open_files": len(current_process.open_files()),
|
139
|
+
"status": current_process.status(),
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
131
143
|
# Monitor overall system
|
132
144
|
if self.track_system:
|
133
145
|
system_cpu = psutil.cpu_percent(interval=None)
|
134
146
|
system_memory = psutil.virtual_memory()
|
135
|
-
system_disk = psutil.disk_usage(
|
136
|
-
|
137
|
-
self.system_data.append(
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
147
|
+
system_disk = psutil.disk_usage("/")
|
148
|
+
|
149
|
+
self.system_data.append(
|
150
|
+
{
|
151
|
+
"timestamp": timestamp,
|
152
|
+
"cpu_percent": system_cpu,
|
153
|
+
"memory_total": system_memory.total,
|
154
|
+
"memory_available": system_memory.available,
|
155
|
+
"memory_used": system_memory.used,
|
156
|
+
"memory_percent": system_memory.percent,
|
157
|
+
"disk_total": system_disk.total,
|
158
|
+
"disk_used": system_disk.used,
|
159
|
+
"disk_percent": system_disk.percent,
|
160
|
+
}
|
161
|
+
)
|
162
|
+
|
149
163
|
except Exception as e:
|
150
164
|
logger.error(f"Error monitoring resources: {str(e)}")
|
151
|
-
|
165
|
+
|
152
166
|
# Sleep until next sampling interval
|
153
167
|
time.sleep(self.sampling_interval)
|
154
|
-
|
168
|
+
|
155
169
|
@contextmanager
|
156
170
|
def monitor(self):
|
157
171
|
"""
|
158
172
|
Context manager for monitoring resources during a block of code.
|
159
|
-
|
173
|
+
|
160
174
|
Example:
|
161
175
|
with resource_monitor.monitor():
|
162
176
|
# Code to monitor
|
@@ -167,201 +181,239 @@ class ResourceMonitor:
|
|
167
181
|
yield
|
168
182
|
finally:
|
169
183
|
self.stop()
|
170
|
-
|
184
|
+
|
171
185
|
def get_process_stats(self) -> Dict[str, Any]:
|
172
186
|
"""
|
173
187
|
Get statistics about this process's resource usage.
|
174
|
-
|
188
|
+
|
175
189
|
Returns:
|
176
190
|
Dictionary with process resource usage statistics
|
177
191
|
"""
|
178
192
|
if not self.process_data:
|
179
193
|
return {}
|
180
|
-
|
194
|
+
|
181
195
|
# Extract data series
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
196
|
+
cpu_values = [d["cpu_percent"] for d in self.process_data]
|
197
|
+
memory_values = [
|
198
|
+
d["memory_rss"] / (1024 * 1024) for d in self.process_data
|
199
|
+
] # Convert to MB
|
200
|
+
|
186
201
|
# Calculate statistics
|
187
202
|
stats = {
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
203
|
+
"start_time": self.start_time,
|
204
|
+
"end_time": self.end_time,
|
205
|
+
"duration": self.end_time - self.start_time
|
206
|
+
if self.end_time
|
207
|
+
else None,
|
208
|
+
"sample_count": len(self.process_data),
|
209
|
+
"cpu_min": min(cpu_values) if cpu_values else None,
|
210
|
+
"cpu_max": max(cpu_values) if cpu_values else None,
|
211
|
+
"cpu_avg": sum(cpu_values) / len(cpu_values)
|
212
|
+
if cpu_values
|
213
|
+
else None,
|
214
|
+
"memory_min_mb": min(memory_values) if memory_values else None,
|
215
|
+
"memory_max_mb": max(memory_values) if memory_values else None,
|
216
|
+
"memory_avg_mb": (
|
217
|
+
sum(memory_values) / len(memory_values)
|
218
|
+
if memory_values
|
219
|
+
else None
|
220
|
+
),
|
221
|
+
"thread_max": (
|
222
|
+
max(d["num_threads"] for d in self.process_data)
|
223
|
+
if self.process_data
|
224
|
+
else None
|
225
|
+
),
|
199
226
|
}
|
200
|
-
|
227
|
+
|
201
228
|
return stats
|
202
|
-
|
229
|
+
|
203
230
|
def get_system_stats(self) -> Dict[str, Any]:
|
204
231
|
"""
|
205
232
|
Get statistics about overall system resource usage.
|
206
|
-
|
233
|
+
|
207
234
|
Returns:
|
208
235
|
Dictionary with system resource usage statistics
|
209
236
|
"""
|
210
237
|
if not self.system_data:
|
211
238
|
return {}
|
212
|
-
|
239
|
+
|
213
240
|
# Extract data series
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
241
|
+
cpu_values = [d["cpu_percent"] for d in self.system_data]
|
242
|
+
memory_values = [d["memory_percent"] for d in self.system_data]
|
243
|
+
disk_values = [d["disk_percent"] for d in self.system_data]
|
244
|
+
|
219
245
|
# Calculate statistics
|
220
246
|
stats = {
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
247
|
+
"start_time": self.start_time,
|
248
|
+
"end_time": self.end_time,
|
249
|
+
"duration": self.end_time - self.start_time
|
250
|
+
if self.end_time
|
251
|
+
else None,
|
252
|
+
"sample_count": len(self.system_data),
|
253
|
+
"cpu_min": min(cpu_values) if cpu_values else None,
|
254
|
+
"cpu_max": max(cpu_values) if cpu_values else None,
|
255
|
+
"cpu_avg": sum(cpu_values) / len(cpu_values)
|
256
|
+
if cpu_values
|
257
|
+
else None,
|
258
|
+
"memory_min_percent": min(memory_values) if memory_values else None,
|
259
|
+
"memory_max_percent": max(memory_values) if memory_values else None,
|
260
|
+
"memory_avg_percent": (
|
261
|
+
sum(memory_values) / len(memory_values)
|
262
|
+
if memory_values
|
263
|
+
else None
|
264
|
+
),
|
265
|
+
"disk_min_percent": min(disk_values) if disk_values else None,
|
266
|
+
"disk_max_percent": max(disk_values) if disk_values else None,
|
267
|
+
"disk_avg_percent": (
|
268
|
+
sum(disk_values) / len(disk_values) if disk_values else None
|
269
|
+
),
|
270
|
+
"memory_total_gb": (
|
271
|
+
self.system_data[0]["memory_total"] / (1024**3)
|
272
|
+
if self.system_data
|
273
|
+
else None
|
274
|
+
),
|
275
|
+
"disk_total_gb": (
|
276
|
+
self.system_data[0]["disk_total"] / (1024**3)
|
277
|
+
if self.system_data
|
278
|
+
else None
|
279
|
+
),
|
236
280
|
}
|
237
|
-
|
281
|
+
|
238
282
|
return stats
|
239
|
-
|
283
|
+
|
240
284
|
def get_combined_stats(self) -> Dict[str, Any]:
|
241
285
|
"""
|
242
286
|
Get combined resource usage statistics.
|
243
|
-
|
287
|
+
|
244
288
|
Returns:
|
245
289
|
Dictionary with both process and system statistics
|
246
290
|
"""
|
247
291
|
process_stats = self.get_process_stats()
|
248
292
|
system_stats = self.get_system_stats()
|
249
|
-
|
293
|
+
|
250
294
|
# Combine stats
|
251
295
|
stats = {
|
252
|
-
|
253
|
-
|
254
|
-
|
296
|
+
"start_time": self.start_time,
|
297
|
+
"end_time": self.end_time,
|
298
|
+
"duration": self.end_time - self.start_time
|
299
|
+
if self.end_time
|
300
|
+
else None,
|
255
301
|
}
|
256
|
-
|
302
|
+
|
257
303
|
# Add process stats with 'process_' prefix
|
258
304
|
for key, value in process_stats.items():
|
259
|
-
if key not in [
|
260
|
-
stats[f
|
261
|
-
|
305
|
+
if key not in ["start_time", "end_time", "duration"]:
|
306
|
+
stats[f"process_{key}"] = value
|
307
|
+
|
262
308
|
# Add system stats with 'system_' prefix
|
263
309
|
for key, value in system_stats.items():
|
264
|
-
if key not in [
|
265
|
-
stats[f
|
266
|
-
|
310
|
+
if key not in ["start_time", "end_time", "duration"]:
|
311
|
+
stats[f"system_{key}"] = value
|
312
|
+
|
267
313
|
# Calculate derived metrics
|
268
|
-
if (
|
269
|
-
|
270
|
-
|
314
|
+
if (
|
315
|
+
process_stats.get("memory_max_mb") is not None
|
316
|
+
and system_stats.get("memory_total_gb") is not None
|
317
|
+
):
|
271
318
|
# Process memory as percentage of total system memory
|
272
|
-
system_memory_mb = system_stats[
|
273
|
-
stats[
|
274
|
-
(process_stats[
|
275
|
-
if system_memory_mb > 0
|
319
|
+
system_memory_mb = system_stats["memory_total_gb"] * 1024
|
320
|
+
stats["process_memory_percent"] = (
|
321
|
+
(process_stats["memory_max_mb"] / system_memory_mb) * 100
|
322
|
+
if system_memory_mb > 0
|
323
|
+
else 0
|
276
324
|
)
|
277
|
-
|
325
|
+
|
278
326
|
return stats
|
279
|
-
|
327
|
+
|
280
328
|
def print_summary(self):
|
281
329
|
"""Print a formatted summary of resource usage."""
|
282
330
|
process_stats = self.get_process_stats()
|
283
331
|
system_stats = self.get_system_stats()
|
284
|
-
|
332
|
+
|
285
333
|
print("\n===== RESOURCE USAGE SUMMARY =====")
|
286
|
-
|
334
|
+
|
287
335
|
if process_stats:
|
288
336
|
print("\n--- Process Resources ---")
|
289
|
-
print(
|
290
|
-
|
291
|
-
|
292
|
-
|
337
|
+
print(
|
338
|
+
f"CPU usage: {process_stats.get('cpu_avg', 0):.1f}% avg, "
|
339
|
+
f"{process_stats.get('cpu_max', 0):.1f}% peak"
|
340
|
+
)
|
341
|
+
print(
|
342
|
+
f"Memory usage: {process_stats.get('memory_avg_mb', 0):.1f} MB avg, "
|
343
|
+
f"{process_stats.get('memory_max_mb', 0):.1f} MB peak"
|
344
|
+
)
|
293
345
|
print(f"Threads: {process_stats.get('thread_max', 0)} max")
|
294
|
-
|
346
|
+
|
295
347
|
if system_stats:
|
296
348
|
print("\n--- System Resources ---")
|
297
|
-
print(
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
349
|
+
print(
|
350
|
+
f"CPU usage: {system_stats.get('cpu_avg', 0):.1f}% avg, "
|
351
|
+
f"{system_stats.get('cpu_max', 0):.1f}% peak"
|
352
|
+
)
|
353
|
+
print(
|
354
|
+
f"Memory usage: {system_stats.get('memory_avg_percent', 0):.1f}% avg, "
|
355
|
+
f"{system_stats.get('memory_max_percent', 0):.1f}% peak "
|
356
|
+
f"(Total: {system_stats.get('memory_total_gb', 0):.1f} GB)"
|
357
|
+
)
|
358
|
+
print(
|
359
|
+
f"Disk usage: {system_stats.get('disk_avg_percent', 0):.1f}% avg "
|
360
|
+
f"(Total: {system_stats.get('disk_total_gb', 0):.1f} GB)"
|
361
|
+
)
|
362
|
+
|
305
363
|
print("\n===================================")
|
306
|
-
|
364
|
+
|
307
365
|
def export_data(self) -> Dict[str, Any]:
|
308
366
|
"""
|
309
367
|
Export all collected data.
|
310
|
-
|
368
|
+
|
311
369
|
Returns:
|
312
370
|
Dictionary with all collected resource usage data
|
313
371
|
"""
|
314
372
|
return {
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
373
|
+
"start_time": self.start_time,
|
374
|
+
"end_time": self.end_time,
|
375
|
+
"sampling_interval": self.sampling_interval,
|
376
|
+
"process_data": self.process_data,
|
377
|
+
"system_data": self.system_data,
|
320
378
|
}
|
321
379
|
|
322
380
|
|
323
381
|
def check_system_resources() -> Dict[str, Any]:
|
324
382
|
"""
|
325
383
|
Check current system resources.
|
326
|
-
|
384
|
+
|
327
385
|
Returns:
|
328
386
|
Dictionary with current resource usage information
|
329
387
|
"""
|
330
388
|
if not PSUTIL_AVAILABLE:
|
331
|
-
return {
|
332
|
-
|
333
|
-
'available': False
|
334
|
-
}
|
335
|
-
|
389
|
+
return {"error": "psutil not available", "available": False}
|
390
|
+
|
336
391
|
try:
|
337
392
|
# Get basic system information
|
338
393
|
cpu_count = psutil.cpu_count(logical=True)
|
339
394
|
cpu_physical = psutil.cpu_count(logical=False)
|
340
395
|
cpu_percent = psutil.cpu_percent(interval=0.1)
|
341
|
-
|
396
|
+
|
342
397
|
memory = psutil.virtual_memory()
|
343
|
-
disk = psutil.disk_usage(
|
344
|
-
|
398
|
+
disk = psutil.disk_usage("/")
|
399
|
+
|
345
400
|
# Format results
|
346
401
|
result = {
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
402
|
+
"available": True,
|
403
|
+
"cpu_count": cpu_count,
|
404
|
+
"cpu_physical": cpu_physical,
|
405
|
+
"cpu_percent": cpu_percent,
|
406
|
+
"memory_total_gb": memory.total / (1024**3),
|
407
|
+
"memory_available_gb": memory.available / (1024**3),
|
408
|
+
"memory_used_gb": memory.used / (1024**3),
|
409
|
+
"memory_percent": memory.percent,
|
410
|
+
"disk_total_gb": disk.total / (1024**3),
|
411
|
+
"disk_free_gb": disk.free / (1024**3),
|
412
|
+
"disk_percent": disk.percent,
|
358
413
|
}
|
359
|
-
|
414
|
+
|
360
415
|
return result
|
361
|
-
|
416
|
+
|
362
417
|
except Exception as e:
|
363
418
|
logger.error(f"Error checking system resources: {str(e)}")
|
364
|
-
return {
|
365
|
-
'error': str(e),
|
366
|
-
'available': False
|
367
|
-
}
|
419
|
+
return {"error": str(e), "available": False}
|