nucleus-mcp 1.0.7 → 1.2.0

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.
Files changed (135) hide show
  1. package/package.json +2 -4
  2. package/.github/workflows/ci.yml +0 -72
  3. package/.smithery/shttp/module.js +0 -3
  4. package/.smithery/shttp/module.js.map +0 -7
  5. package/.smithery/stdio/index.cjs +0 -74
  6. package/.smithery/stdio/index.cjs.map +0 -7
  7. package/CHANGELOG.md +0 -65
  8. package/CODE_OF_CONDUCT.md +0 -34
  9. package/COMPARISON.md +0 -20
  10. package/CONTRIBUTING.md +0 -137
  11. package/DEVELOPMENT.md +0 -109
  12. package/LICENSE +0 -21
  13. package/README.md +0 -319
  14. package/ROADMAP.md +0 -24
  15. package/SECURITY.md +0 -88
  16. package/docs/COMMUNITY_FAQ.md +0 -48
  17. package/docs/COMPETITIVE_ANALYSIS.md +0 -90
  18. package/docs/DEMO_SCRIPT.md +0 -86
  19. package/docs/ENTERPRISE.md +0 -169
  20. package/docs/FAQ.md +0 -228
  21. package/docs/QUICK_START.md +0 -155
  22. package/docs/README.md +0 -67
  23. package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
  24. package/docs/social-preview-hq.png +0 -0
  25. package/docs/social-preview.png +0 -0
  26. package/examples/basic_usage.py +0 -84
  27. package/package.json.real +0 -24
  28. package/pyproject.toml +0 -50
  29. package/registry-server.json +0 -20
  30. package/scripts/demo_governance.py +0 -109
  31. package/scripts/sse_bridge.py +0 -87
  32. package/scripts/verify_windows_paths.py +0 -48
  33. package/server.json +0 -20
  34. package/src/mcp_server_nucleus/__init__.py +0 -8583
  35. package/src/mcp_server_nucleus/__main__.py +0 -9
  36. package/src/mcp_server_nucleus/cli.py +0 -1225
  37. package/src/mcp_server_nucleus/commitment_core.py +0 -146
  38. package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
  39. package/src/mcp_server_nucleus/core/__init__.py +0 -6
  40. package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
  41. package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
  42. package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
  43. package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
  44. package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
  45. package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
  46. package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
  47. package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
  48. package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
  49. package/src/mcp_server_nucleus/runtime/agent.py +0 -517
  50. package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
  51. package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
  52. package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
  53. package/src/mcp_server_nucleus/runtime/auth.py +0 -98
  54. package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
  55. package/src/mcp_server_nucleus/runtime/broker.py +0 -126
  56. package/src/mcp_server_nucleus/runtime/budget.py +0 -153
  57. package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
  58. package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
  59. package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
  60. package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
  61. package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
  62. package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
  63. package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
  64. package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
  65. package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
  66. package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
  67. package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
  68. package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
  69. package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
  70. package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
  71. package/src/mcp_server_nucleus/runtime/common.py +0 -134
  72. package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
  73. package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
  74. package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
  75. package/src/mcp_server_nucleus/runtime/critic.py +0 -150
  76. package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
  77. package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
  78. package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
  79. package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
  80. package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
  81. package/src/mcp_server_nucleus/runtime/export.py +0 -64
  82. package/src/mcp_server_nucleus/runtime/factory.py +0 -594
  83. package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
  84. package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
  85. package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
  86. package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
  87. package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
  88. package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
  89. package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
  90. package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
  91. package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
  92. package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
  93. package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
  94. package/src/mcp_server_nucleus/runtime/installer.py +0 -81
  95. package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
  96. package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
  97. package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
  98. package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
  99. package/src/mcp_server_nucleus/runtime/locking.py +0 -228
  100. package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
  101. package/src/mcp_server_nucleus/runtime/memory.py +0 -158
  102. package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
  103. package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
  104. package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
  105. package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
  106. package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
  107. package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
  108. package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
  109. package/src/mcp_server_nucleus/runtime/policy.py +0 -117
  110. package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
  111. package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
  112. package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
  113. package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
  114. package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
  115. package/src/mcp_server_nucleus/runtime/registry.py +0 -82
  116. package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
  117. package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
  118. package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
  119. package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
  120. package/src/mcp_server_nucleus/runtime/storage.py +0 -83
  121. package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
  122. package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
  123. package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
  124. package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
  125. package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
  126. package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
  127. package/src/mcp_server_nucleus/runtime/team.py +0 -59
  128. package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
  129. package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
  130. package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
  131. package/src/mcp_server_nucleus/tool_tiers.py +0 -243
  132. package/tests/__init__.py +0 -1
  133. package/tests/conftest.py +0 -42
  134. package/tests/test_core.py +0 -235
  135. package/tests/test_search.py +0 -69
@@ -1,84 +0,0 @@
1
- """
2
- PluginLoader v2: The Airlock.
3
- Strictly controls how tools enter the Nucleus Runtime.
4
-
5
- Strategic Role:
6
- - Enforces "Air Gap": Only loads tools explicitly allowed by Manifest.
7
- - Enforces "Governance": Wraps all tools in BudgetGuard.
8
- - Isolates Agents: Scopes loading to specific Agent Directory.
9
- """
10
-
11
- import sys
12
- import importlib.util
13
- import logging
14
- from pathlib import Path
15
- from typing import List
16
- from .capabilities.base import Capability
17
- from .budget import BudgetAuditor, BudgetGuard
18
-
19
- logger = logging.getLogger("nucleus.plugins")
20
-
21
- class PluginLoader:
22
- def __init__(self, brain_path: Path, auditor: BudgetAuditor):
23
- self.brain_path = brain_path
24
- self.auditor = auditor
25
- self.installed_tools_dir = brain_path / "tools" / "installed"
26
-
27
- def load_agent_tools(self, agent_id: str, authorized_modules: List[str]) -> List[Capability]:
28
- """
29
- Loads allowed tools for a specific agent.
30
- Wraps them in BudgetGuard(default=$0.00).
31
- """
32
- agent_dir = self.installed_tools_dir / agent_id
33
-
34
- if not agent_dir.exists():
35
- logger.warning(f"⚠️ Agent directory not found: {agent_dir}")
36
- return []
37
-
38
- logger.info(f"🔌 Accessing Agent Airlock: {agent_id}")
39
-
40
- # Ensure the directory is in python path
41
- sys.path.append(str(agent_dir))
42
-
43
- loaded_plugins = []
44
-
45
- for module_name in authorized_modules:
46
- # Construct expected file path
47
- tool_path = agent_dir / f"{module_name}.py"
48
-
49
- if not tool_path.exists():
50
- logger.error(f"❌ Missing authorized tool: {module_name} in {agent_dir}")
51
- continue
52
-
53
- try:
54
- # Import Module
55
- spec = importlib.util.spec_from_file_location(module_name, tool_path)
56
- if spec and spec.loader:
57
- module = importlib.util.module_from_spec(spec)
58
- spec.loader.exec_module(module)
59
-
60
- if hasattr(module, "get_capability"):
61
- cap = module.get_capability()
62
- if isinstance(cap, Capability):
63
-
64
- # --- GOVERNANCE WRAPPER ---
65
- # By default, tools have $0.00 budget until Policy grants more.
66
- # We wrap immediately to ensure safety.
67
- guarded_cap = BudgetGuard(
68
- inner=cap,
69
- auditor=self.auditor,
70
- agent_id=agent_id,
71
- max_budget_usd=0.0 # Default Deny
72
- )
73
-
74
- loaded_plugins.append(guarded_cap)
75
- logger.info(f" ✅ Loaded & Secured: {cap.name}")
76
- else:
77
- logger.warning(f" ⚠️ Invalid Capability in {module_name}")
78
- else:
79
- logger.warning(f" ℹ️ Missing entry point in {module_name}")
80
-
81
- except Exception as e:
82
- logger.error(f" ❌ Failed to load {module_name}: {e}")
83
-
84
- return loaded_plugins
@@ -1,117 +0,0 @@
1
-
2
- """
3
- Policy Engine for Nucleus Daemon.
4
- Handles 'Adaptive Policy' (Directives) and 'Bounded Autonomy' (Mission Parameters).
5
-
6
- Strategic Role:
7
- - Reads `directives.md` to align Daemon with user intent.
8
- - Enforces `MissionParameters` (Budget, Time, Safety) to prevent "Strategic Drift".
9
- - Provides the `SafetyLevel` context for "Unshackled" operations.
10
- """
11
-
12
- from enum import Enum
13
- from dataclasses import dataclass, field
14
- from pathlib import Path
15
- from typing import List, Dict
16
- import json
17
- import logging
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- class SafetyLevel(Enum):
22
- SAFE = "safe" # Standard SaaS-like filters
23
- MODERATE = "moderate" # Less restrictive, warns on high risk
24
- RAW = "raw" # "Unshackled" - No middleware constraints (User liability)
25
-
26
- class ExecutionMode(Enum):
27
- PLANNING = "planning"
28
- EXECUTION = "execution"
29
- VERIFICATION = "verification"
30
-
31
- @dataclass
32
- class MissionParameters:
33
- """The Compact: Bounded Autonomy Agreement"""
34
- max_budget_usd: float = 1.0
35
- max_steps: int = 10
36
- timeout_seconds: int = 600
37
- allowed_tools: List[str] = field(default_factory=list)
38
- blocked_tools: List[str] = field(default_factory=list)
39
- safety_level: SafetyLevel = SafetyLevel.SAFE
40
- # If true, requires explicit human ratification before "Write" actions
41
- require_ratification: bool = False
42
-
43
- class DirectivesLoader:
44
- def __init__(self, brain_path: Path):
45
- self.brain_path = brain_path
46
- self.directives_path = brain_path / "ledger" / "directives.md"
47
- # Support json config as alternative
48
- self.config_path = brain_path / "ledger" / "policy.json"
49
-
50
- def load_policy(self) -> Dict:
51
- """Load the current policy directives"""
52
- policy = {
53
- "mode": "default",
54
- "priorities": [],
55
- "constraints": [],
56
- "safety_level": SafetyLevel.SAFE.value
57
- }
58
-
59
- # 1. Try Config JSON first (Structured)
60
- if self.config_path.exists():
61
- try:
62
- with open(self.config_path, 'r') as f:
63
- data = json.load(f)
64
- if data:
65
- policy.update(data)
66
- except Exception as e:
67
- logger.error(f"Failed to load policy.json: {e}")
68
-
69
- # 2. Parse Markdown Directives (Context)
70
- # For now, we just verify it exists to be available for LLM context injection
71
- # In Phase 60, we might NLP parse strict rules from MD.
72
- policy["directives_text"] = self._read_directives_text()
73
-
74
- return policy
75
-
76
- def _read_directives_text(self) -> str:
77
- if self.directives_path.exists():
78
- return self.directives_path.read_text()
79
- return "No directives found. Operate in default safe mode."
80
-
81
- def get_mission_parameters(self, task_type: str) -> MissionParameters:
82
- """Factory for Mission Parameters based on Task Type"""
83
- policy = self.load_policy()
84
- safety_val = policy.get("safety_level", "safe")
85
-
86
- try:
87
- safety = SafetyLevel(safety_val)
88
- except ValueError:
89
- safety = SafetyLevel.SAFE
90
-
91
- # Default Profiles
92
- if task_type == "quick_check":
93
- return MissionParameters(
94
- max_budget_usd=0.05,
95
- max_steps=5,
96
- timeout_seconds=60,
97
- safety_level=safety
98
- )
99
- elif task_type == "deep_research":
100
- return MissionParameters(
101
- max_budget_usd=2.0,
102
- max_steps=30,
103
- timeout_seconds=1800,
104
- safety_level=safety,
105
- blocked_tools=["write_to_file", "run_command"] # Read only
106
- )
107
- elif task_type == "autonomous_fix":
108
- return MissionParameters(
109
- max_budget_usd=0.5,
110
- max_steps=15,
111
- timeout_seconds=300,
112
- safety_level=safety,
113
- require_ratification=True # Fixes usually need approval in V1
114
- )
115
-
116
- # Default
117
- return MissionParameters(safety_level=safety)
@@ -1,219 +0,0 @@
1
- """
2
- Nucleus Runtime - Performance Profiling (AG-014)
3
- =================================================
4
- Timing decorators and performance monitoring for identifying bottlenecks.
5
-
6
- Usage:
7
- from .profiling import timed, get_metrics, reset_metrics
8
-
9
- @timed("task_list")
10
- def _list_tasks(...):
11
- ...
12
-
13
- # Get metrics
14
- metrics = get_metrics()
15
- print(metrics["task_list"]) # {"calls": 10, "total_ms": 150.5, "avg_ms": 15.05}
16
- """
17
-
18
- import time
19
- import functools
20
- import threading
21
- from typing import Dict, Any, Callable, Optional
22
- from datetime import datetime, timezone
23
- import json
24
- import os
25
- from pathlib import Path
26
-
27
- # Thread-safe metrics storage
28
- _metrics_lock = threading.Lock()
29
- _metrics: Dict[str, Dict[str, Any]] = {}
30
-
31
- # Configuration
32
- PROFILING_ENABLED = os.environ.get("NUCLEUS_PROFILING", "false").lower() == "true"
33
- SLOW_THRESHOLD_MS = float(os.environ.get("NUCLEUS_SLOW_THRESHOLD_MS", "100"))
34
-
35
-
36
- def timed(operation_name: str, log_slow: bool = True):
37
- """
38
- Decorator to time function execution and collect metrics.
39
-
40
- Args:
41
- operation_name: Name for this operation in metrics
42
- log_slow: Whether to log warning for slow operations
43
-
44
- Example:
45
- @timed("brain_list_tasks")
46
- def _list_tasks():
47
- ...
48
- """
49
- def decorator(func: Callable) -> Callable:
50
- @functools.wraps(func)
51
- def wrapper(*args, **kwargs):
52
- if not PROFILING_ENABLED:
53
- return func(*args, **kwargs)
54
-
55
- start = time.perf_counter()
56
- try:
57
- result = func(*args, **kwargs)
58
- return result
59
- finally:
60
- elapsed_ms = (time.perf_counter() - start) * 1000
61
- _record_metric(operation_name, elapsed_ms, log_slow)
62
-
63
- @functools.wraps(func)
64
- async def async_wrapper(*args, **kwargs):
65
- if not PROFILING_ENABLED:
66
- return await func(*args, **kwargs)
67
-
68
- start = time.perf_counter()
69
- try:
70
- result = await func(*args, **kwargs)
71
- return result
72
- finally:
73
- elapsed_ms = (time.perf_counter() - start) * 1000
74
- _record_metric(operation_name, elapsed_ms, log_slow)
75
-
76
- # Return appropriate wrapper based on function type
77
- if asyncio_iscoroutinefunction(func):
78
- return async_wrapper
79
- return wrapper
80
- return decorator
81
-
82
-
83
- def asyncio_iscoroutinefunction(func):
84
- """Check if function is async."""
85
- import inspect
86
- return inspect.iscoroutinefunction(func)
87
-
88
-
89
- def _record_metric(operation_name: str, elapsed_ms: float, log_slow: bool):
90
- """Record a timing metric (thread-safe)."""
91
- with _metrics_lock:
92
- if operation_name not in _metrics:
93
- _metrics[operation_name] = {
94
- "calls": 0,
95
- "total_ms": 0.0,
96
- "min_ms": float('inf'),
97
- "max_ms": 0.0,
98
- "last_call": None,
99
- "slow_count": 0
100
- }
101
-
102
- m = _metrics[operation_name]
103
- m["calls"] += 1
104
- m["total_ms"] += elapsed_ms
105
- m["min_ms"] = min(m["min_ms"], elapsed_ms)
106
- m["max_ms"] = max(m["max_ms"], elapsed_ms)
107
- m["last_call"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
108
-
109
- if elapsed_ms > SLOW_THRESHOLD_MS:
110
- m["slow_count"] += 1
111
- if log_slow:
112
- # Use print for now, can integrate with structured logging
113
- print(f"⚠️ SLOW: {operation_name} took {elapsed_ms:.2f}ms (threshold: {SLOW_THRESHOLD_MS}ms)")
114
-
115
-
116
- def get_metrics() -> Dict[str, Dict[str, Any]]:
117
- """
118
- Get current performance metrics.
119
-
120
- Returns:
121
- Dict with metrics per operation:
122
- {
123
- "operation_name": {
124
- "calls": int,
125
- "total_ms": float,
126
- "avg_ms": float,
127
- "min_ms": float,
128
- "max_ms": float,
129
- "slow_count": int,
130
- "last_call": str (ISO timestamp)
131
- }
132
- }
133
- """
134
- with _metrics_lock:
135
- result = {}
136
- for name, m in _metrics.items():
137
- result[name] = {
138
- **m,
139
- "avg_ms": m["total_ms"] / m["calls"] if m["calls"] > 0 else 0
140
- }
141
- # Clean up inf for JSON serialization
142
- if result[name]["min_ms"] == float('inf'):
143
- result[name]["min_ms"] = 0
144
- return result
145
-
146
-
147
- def reset_metrics():
148
- """Reset all metrics (useful for testing)."""
149
- global _metrics
150
- with _metrics_lock:
151
- _metrics = {}
152
-
153
-
154
- def get_metrics_summary() -> str:
155
- """Get a formatted summary of metrics."""
156
- metrics = get_metrics()
157
- if not metrics:
158
- return "No metrics collected. Set NUCLEUS_PROFILING=true to enable."
159
-
160
- lines = ["# Performance Metrics", ""]
161
- lines.append("| Operation | Calls | Avg (ms) | Min | Max | Slow |")
162
- lines.append("|-----------|-------|----------|-----|-----|------|")
163
-
164
- for name, m in sorted(metrics.items(), key=lambda x: -x[1]["total_ms"]):
165
- lines.append(
166
- f"| {name} | {m['calls']} | {m['avg_ms']:.2f} | "
167
- f"{m['min_ms']:.2f} | {m['max_ms']:.2f} | {m['slow_count']} |"
168
- )
169
-
170
- lines.append("")
171
- lines.append(f"*Slow threshold: {SLOW_THRESHOLD_MS}ms*")
172
- return "\n".join(lines)
173
-
174
-
175
- def export_metrics_to_file(brain_path: Optional[Path] = None) -> str:
176
- """
177
- Export metrics to a JSON file in the brain folder.
178
-
179
- Args:
180
- brain_path: Path to brain folder (uses env var if not provided)
181
-
182
- Returns:
183
- Path to exported file
184
- """
185
- if brain_path is None:
186
- brain_path = Path(os.environ.get("NUCLEAR_BRAIN_PATH", ".brain"))
187
-
188
- metrics_dir = brain_path / "metrics"
189
- metrics_dir.mkdir(parents=True, exist_ok=True)
190
-
191
- timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
192
- filepath = metrics_dir / f"perf_{timestamp}.json"
193
-
194
- data = {
195
- "exported_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
196
- "threshold_ms": SLOW_THRESHOLD_MS,
197
- "metrics": get_metrics()
198
- }
199
-
200
- with open(filepath, 'w') as f:
201
- json.dump(data, f, indent=2)
202
-
203
- return str(filepath)
204
-
205
-
206
- # Convenience decorators for common operations
207
- def timed_io(func):
208
- """Decorator for I/O-bound operations (file reads, etc.)."""
209
- return timed(f"io.{func.__name__}", log_slow=True)(func)
210
-
211
-
212
- def timed_compute(func):
213
- """Decorator for compute-bound operations."""
214
- return timed(f"compute.{func.__name__}", log_slow=True)(func)
215
-
216
-
217
- def timed_llm(func):
218
- """Decorator for LLM API calls (expected to be slow)."""
219
- return timed(f"llm.{func.__name__}", log_slow=False)(func)