empathy-framework 3.2.3__py3-none-any.whl → 3.8.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.
Files changed (328) hide show
  1. coach_wizards/__init__.py +11 -12
  2. coach_wizards/accessibility_wizard.py +12 -12
  3. coach_wizards/api_wizard.py +12 -12
  4. coach_wizards/base_wizard.py +26 -20
  5. coach_wizards/cicd_wizard.py +15 -13
  6. coach_wizards/code_reviewer_README.md +60 -0
  7. coach_wizards/code_reviewer_wizard.py +180 -0
  8. coach_wizards/compliance_wizard.py +12 -12
  9. coach_wizards/database_wizard.py +12 -12
  10. coach_wizards/debugging_wizard.py +12 -12
  11. coach_wizards/documentation_wizard.py +12 -12
  12. coach_wizards/generate_wizards.py +1 -2
  13. coach_wizards/localization_wizard.py +101 -19
  14. coach_wizards/migration_wizard.py +12 -12
  15. coach_wizards/monitoring_wizard.py +12 -12
  16. coach_wizards/observability_wizard.py +12 -12
  17. coach_wizards/performance_wizard.py +12 -12
  18. coach_wizards/prompt_engineering_wizard.py +22 -25
  19. coach_wizards/refactoring_wizard.py +12 -12
  20. coach_wizards/scaling_wizard.py +12 -12
  21. coach_wizards/security_wizard.py +12 -12
  22. coach_wizards/testing_wizard.py +12 -12
  23. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/METADATA +513 -58
  24. empathy_framework-3.8.2.dist-info/RECORD +333 -0
  25. empathy_framework-3.8.2.dist-info/entry_points.txt +22 -0
  26. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/top_level.txt +5 -1
  27. empathy_healthcare_plugin/__init__.py +1 -2
  28. empathy_healthcare_plugin/monitors/__init__.py +9 -0
  29. empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
  30. empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
  31. empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
  32. empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
  33. empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
  34. empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
  35. empathy_llm_toolkit/__init__.py +7 -7
  36. empathy_llm_toolkit/agent_factory/__init__.py +53 -0
  37. empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
  38. empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
  39. empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
  40. empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
  41. empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
  42. empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
  43. empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
  44. empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
  45. empathy_llm_toolkit/agent_factory/base.py +305 -0
  46. empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
  47. empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
  48. empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
  49. empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
  50. empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
  51. empathy_llm_toolkit/agent_factory/decorators.py +286 -0
  52. empathy_llm_toolkit/agent_factory/factory.py +558 -0
  53. empathy_llm_toolkit/agent_factory/framework.py +192 -0
  54. empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
  55. empathy_llm_toolkit/agent_factory/resilient.py +320 -0
  56. empathy_llm_toolkit/claude_memory.py +14 -15
  57. empathy_llm_toolkit/cli/__init__.py +8 -0
  58. empathy_llm_toolkit/cli/sync_claude.py +487 -0
  59. empathy_llm_toolkit/code_health.py +177 -22
  60. empathy_llm_toolkit/config/__init__.py +29 -0
  61. empathy_llm_toolkit/config/unified.py +295 -0
  62. empathy_llm_toolkit/contextual_patterns.py +11 -12
  63. empathy_llm_toolkit/core.py +51 -49
  64. empathy_llm_toolkit/git_pattern_extractor.py +16 -12
  65. empathy_llm_toolkit/levels.py +6 -13
  66. empathy_llm_toolkit/pattern_confidence.py +14 -18
  67. empathy_llm_toolkit/pattern_resolver.py +10 -12
  68. empathy_llm_toolkit/pattern_summary.py +13 -11
  69. empathy_llm_toolkit/providers.py +194 -28
  70. empathy_llm_toolkit/routing/__init__.py +32 -0
  71. empathy_llm_toolkit/routing/model_router.py +362 -0
  72. empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
  73. empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
  74. empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
  75. empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
  76. empathy_llm_toolkit/security/README.md +262 -0
  77. empathy_llm_toolkit/security/__init__.py +62 -0
  78. empathy_llm_toolkit/security/audit_logger.py +929 -0
  79. empathy_llm_toolkit/security/audit_logger_example.py +152 -0
  80. empathy_llm_toolkit/security/pii_scrubber.py +640 -0
  81. empathy_llm_toolkit/security/secrets_detector.py +678 -0
  82. empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
  83. empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
  84. empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
  85. empathy_llm_toolkit/session_status.py +18 -20
  86. empathy_llm_toolkit/state.py +20 -21
  87. empathy_llm_toolkit/wizards/__init__.py +38 -0
  88. empathy_llm_toolkit/wizards/base_wizard.py +364 -0
  89. empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
  90. empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
  91. empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
  92. empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
  93. empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
  94. empathy_os/__init__.py +76 -77
  95. empathy_os/adaptive/__init__.py +13 -0
  96. empathy_os/adaptive/task_complexity.py +127 -0
  97. empathy_os/{monitoring.py → agent_monitoring.py} +27 -27
  98. empathy_os/cache/__init__.py +117 -0
  99. empathy_os/cache/base.py +166 -0
  100. empathy_os/cache/dependency_manager.py +253 -0
  101. empathy_os/cache/hash_only.py +248 -0
  102. empathy_os/cache/hybrid.py +390 -0
  103. empathy_os/cache/storage.py +282 -0
  104. empathy_os/cli.py +515 -109
  105. empathy_os/cli_unified.py +189 -42
  106. empathy_os/config/__init__.py +63 -0
  107. empathy_os/config/xml_config.py +239 -0
  108. empathy_os/config.py +87 -36
  109. empathy_os/coordination.py +48 -54
  110. empathy_os/core.py +90 -99
  111. empathy_os/cost_tracker.py +20 -23
  112. empathy_os/dashboard/__init__.py +15 -0
  113. empathy_os/dashboard/server.py +743 -0
  114. empathy_os/discovery.py +9 -11
  115. empathy_os/emergence.py +20 -21
  116. empathy_os/exceptions.py +18 -30
  117. empathy_os/feedback_loops.py +27 -30
  118. empathy_os/levels.py +31 -34
  119. empathy_os/leverage_points.py +27 -28
  120. empathy_os/logging_config.py +11 -12
  121. empathy_os/memory/__init__.py +195 -0
  122. empathy_os/memory/claude_memory.py +466 -0
  123. empathy_os/memory/config.py +224 -0
  124. empathy_os/memory/control_panel.py +1298 -0
  125. empathy_os/memory/edges.py +179 -0
  126. empathy_os/memory/graph.py +567 -0
  127. empathy_os/memory/long_term.py +1194 -0
  128. empathy_os/memory/nodes.py +179 -0
  129. empathy_os/memory/redis_bootstrap.py +540 -0
  130. empathy_os/memory/security/__init__.py +31 -0
  131. empathy_os/memory/security/audit_logger.py +930 -0
  132. empathy_os/memory/security/pii_scrubber.py +640 -0
  133. empathy_os/memory/security/secrets_detector.py +678 -0
  134. empathy_os/memory/short_term.py +2119 -0
  135. empathy_os/memory/storage/__init__.py +15 -0
  136. empathy_os/memory/summary_index.py +583 -0
  137. empathy_os/memory/unified.py +619 -0
  138. empathy_os/metrics/__init__.py +12 -0
  139. empathy_os/metrics/prompt_metrics.py +190 -0
  140. empathy_os/models/__init__.py +136 -0
  141. empathy_os/models/__main__.py +13 -0
  142. empathy_os/models/cli.py +655 -0
  143. empathy_os/models/empathy_executor.py +354 -0
  144. empathy_os/models/executor.py +252 -0
  145. empathy_os/models/fallback.py +671 -0
  146. empathy_os/models/provider_config.py +563 -0
  147. empathy_os/models/registry.py +382 -0
  148. empathy_os/models/tasks.py +302 -0
  149. empathy_os/models/telemetry.py +548 -0
  150. empathy_os/models/token_estimator.py +378 -0
  151. empathy_os/models/validation.py +274 -0
  152. empathy_os/monitoring/__init__.py +52 -0
  153. empathy_os/monitoring/alerts.py +23 -0
  154. empathy_os/monitoring/alerts_cli.py +268 -0
  155. empathy_os/monitoring/multi_backend.py +271 -0
  156. empathy_os/monitoring/otel_backend.py +363 -0
  157. empathy_os/optimization/__init__.py +19 -0
  158. empathy_os/optimization/context_optimizer.py +272 -0
  159. empathy_os/pattern_library.py +29 -28
  160. empathy_os/persistence.py +30 -34
  161. empathy_os/platform_utils.py +261 -0
  162. empathy_os/plugins/__init__.py +28 -0
  163. empathy_os/plugins/base.py +361 -0
  164. empathy_os/plugins/registry.py +268 -0
  165. empathy_os/project_index/__init__.py +30 -0
  166. empathy_os/project_index/cli.py +335 -0
  167. empathy_os/project_index/crew_integration.py +430 -0
  168. empathy_os/project_index/index.py +425 -0
  169. empathy_os/project_index/models.py +501 -0
  170. empathy_os/project_index/reports.py +473 -0
  171. empathy_os/project_index/scanner.py +538 -0
  172. empathy_os/prompts/__init__.py +61 -0
  173. empathy_os/prompts/config.py +77 -0
  174. empathy_os/prompts/context.py +177 -0
  175. empathy_os/prompts/parser.py +285 -0
  176. empathy_os/prompts/registry.py +313 -0
  177. empathy_os/prompts/templates.py +208 -0
  178. empathy_os/redis_config.py +144 -58
  179. empathy_os/redis_memory.py +53 -56
  180. empathy_os/resilience/__init__.py +56 -0
  181. empathy_os/resilience/circuit_breaker.py +256 -0
  182. empathy_os/resilience/fallback.py +179 -0
  183. empathy_os/resilience/health.py +300 -0
  184. empathy_os/resilience/retry.py +209 -0
  185. empathy_os/resilience/timeout.py +135 -0
  186. empathy_os/routing/__init__.py +43 -0
  187. empathy_os/routing/chain_executor.py +433 -0
  188. empathy_os/routing/classifier.py +217 -0
  189. empathy_os/routing/smart_router.py +234 -0
  190. empathy_os/routing/wizard_registry.py +307 -0
  191. empathy_os/templates.py +12 -11
  192. empathy_os/trust/__init__.py +28 -0
  193. empathy_os/trust/circuit_breaker.py +579 -0
  194. empathy_os/trust_building.py +44 -36
  195. empathy_os/validation/__init__.py +19 -0
  196. empathy_os/validation/xml_validator.py +281 -0
  197. empathy_os/wizard_factory_cli.py +170 -0
  198. empathy_os/{workflows.py → workflow_commands.py} +123 -31
  199. empathy_os/workflows/__init__.py +360 -0
  200. empathy_os/workflows/base.py +1660 -0
  201. empathy_os/workflows/bug_predict.py +962 -0
  202. empathy_os/workflows/code_review.py +960 -0
  203. empathy_os/workflows/code_review_adapters.py +310 -0
  204. empathy_os/workflows/code_review_pipeline.py +720 -0
  205. empathy_os/workflows/config.py +600 -0
  206. empathy_os/workflows/dependency_check.py +648 -0
  207. empathy_os/workflows/document_gen.py +1069 -0
  208. empathy_os/workflows/documentation_orchestrator.py +1205 -0
  209. empathy_os/workflows/health_check.py +679 -0
  210. empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
  211. empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
  212. empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
  213. empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
  214. empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
  215. empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
  216. empathy_os/workflows/manage_documentation.py +804 -0
  217. empathy_os/workflows/new_sample_workflow1.py +146 -0
  218. empathy_os/workflows/new_sample_workflow1_README.md +150 -0
  219. empathy_os/workflows/perf_audit.py +687 -0
  220. empathy_os/workflows/pr_review.py +748 -0
  221. empathy_os/workflows/progress.py +445 -0
  222. empathy_os/workflows/progress_server.py +322 -0
  223. empathy_os/workflows/refactor_plan.py +693 -0
  224. empathy_os/workflows/release_prep.py +808 -0
  225. empathy_os/workflows/research_synthesis.py +404 -0
  226. empathy_os/workflows/secure_release.py +585 -0
  227. empathy_os/workflows/security_adapters.py +297 -0
  228. empathy_os/workflows/security_audit.py +1046 -0
  229. empathy_os/workflows/step_config.py +234 -0
  230. empathy_os/workflows/test5.py +125 -0
  231. empathy_os/workflows/test5_README.md +158 -0
  232. empathy_os/workflows/test_gen.py +1855 -0
  233. empathy_os/workflows/test_lifecycle.py +526 -0
  234. empathy_os/workflows/test_maintenance.py +626 -0
  235. empathy_os/workflows/test_maintenance_cli.py +590 -0
  236. empathy_os/workflows/test_maintenance_crew.py +821 -0
  237. empathy_os/workflows/xml_enhanced_crew.py +285 -0
  238. empathy_software_plugin/__init__.py +1 -2
  239. empathy_software_plugin/cli/__init__.py +120 -0
  240. empathy_software_plugin/cli/inspect.py +362 -0
  241. empathy_software_plugin/cli.py +35 -26
  242. empathy_software_plugin/plugin.py +4 -8
  243. empathy_software_plugin/wizards/__init__.py +42 -0
  244. empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
  245. empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
  246. empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
  247. empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
  248. empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
  249. empathy_software_plugin/wizards/base_wizard.py +288 -0
  250. empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
  251. empathy_software_plugin/wizards/code_review_wizard.py +606 -0
  252. empathy_software_plugin/wizards/debugging/__init__.py +50 -0
  253. empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
  254. empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
  255. empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
  256. empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
  257. empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
  258. empathy_software_plugin/wizards/debugging/verification.py +369 -0
  259. empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
  260. empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
  261. empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
  262. empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
  263. empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
  264. empathy_software_plugin/wizards/performance/__init__.py +9 -0
  265. empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
  266. empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
  267. empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
  268. empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
  269. empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
  270. empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
  271. empathy_software_plugin/wizards/security/__init__.py +32 -0
  272. empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
  273. empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
  274. empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
  275. empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
  276. empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
  277. empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
  278. empathy_software_plugin/wizards/testing/__init__.py +27 -0
  279. empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
  280. empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
  281. empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
  282. empathy_software_plugin/wizards/testing_wizard.py +274 -0
  283. hot_reload/README.md +473 -0
  284. hot_reload/__init__.py +62 -0
  285. hot_reload/config.py +84 -0
  286. hot_reload/integration.py +228 -0
  287. hot_reload/reloader.py +298 -0
  288. hot_reload/watcher.py +179 -0
  289. hot_reload/websocket.py +176 -0
  290. scaffolding/README.md +589 -0
  291. scaffolding/__init__.py +35 -0
  292. scaffolding/__main__.py +14 -0
  293. scaffolding/cli.py +240 -0
  294. test_generator/__init__.py +38 -0
  295. test_generator/__main__.py +14 -0
  296. test_generator/cli.py +226 -0
  297. test_generator/generator.py +325 -0
  298. test_generator/risk_analyzer.py +216 -0
  299. workflow_patterns/__init__.py +33 -0
  300. workflow_patterns/behavior.py +249 -0
  301. workflow_patterns/core.py +76 -0
  302. workflow_patterns/output.py +99 -0
  303. workflow_patterns/registry.py +255 -0
  304. workflow_patterns/structural.py +288 -0
  305. workflow_scaffolding/__init__.py +11 -0
  306. workflow_scaffolding/__main__.py +12 -0
  307. workflow_scaffolding/cli.py +206 -0
  308. workflow_scaffolding/generator.py +265 -0
  309. agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
  310. agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
  311. agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
  312. agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
  313. agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
  314. agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
  315. agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
  316. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
  317. agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
  318. agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
  319. agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
  320. agents/compliance_anticipation_agent.py +0 -1427
  321. agents/epic_integration_wizard.py +0 -541
  322. agents/trust_building_behaviors.py +0 -891
  323. empathy_framework-3.2.3.dist-info/RECORD +0 -104
  324. empathy_framework-3.2.3.dist-info/entry_points.txt +0 -7
  325. empathy_llm_toolkit/htmlcov/status.json +0 -1
  326. empathy_llm_toolkit/security/htmlcov/status.json +0 -1
  327. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/WHEEL +0 -0
  328. {empathy_framework-3.2.3.dist-info → empathy_framework-3.8.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,23 @@
1
+ """Alert System for LLM Telemetry Monitoring
2
+
3
+ Provides threshold-based alerting for LLM usage metrics.
4
+
5
+ **Features:**
6
+ - Interactive CLI wizard (`empathy alerts init`)
7
+ - Multiple notification channels (webhook, email, stdout)
8
+ - Threshold triggers (daily cost, error rate, etc.)
9
+ - Cooldown mechanism (prevent spam)
10
+ - Enterprise background daemon (`empathy alerts watch --daemon`)
11
+
12
+ **Implementation Status:** Sprint 3 (Week 3)
13
+
14
+ Copyright 2025 Smart-AI-Memory
15
+ Licensed under Fair Source License 0.9
16
+ """
17
+
18
+ # TODO: Implement in Sprint 3
19
+ # - AlertEngine class
20
+ # - CLI wizard for setup
21
+ # - Background watcher (optional daemon)
22
+ # - Webhook delivery
23
+ # - Email notifications
@@ -0,0 +1,268 @@
1
+ """Alert CLI Wizard
2
+
3
+ Interactive wizard for setting up LLM telemetry alerts.
4
+
5
+ **Usage:**
6
+ empathy alerts init
7
+ empathy alerts list
8
+ empathy alerts delete <id>
9
+ empathy alerts watch [--daemon]
10
+
11
+ **Implementation:** Sprint 3 (Week 3)
12
+
13
+ Copyright 2025 Smart-AI-Memory
14
+ Licensed under Fair Source License 0.9
15
+ """
16
+
17
+ import sqlite3
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ import click
22
+
23
+
24
+ class AlertEngine:
25
+ """Alert engine with SQLite storage"""
26
+
27
+ def __init__(self, db_path: str = ".empathy/alerts.db"):
28
+ self.db_path = Path(db_path)
29
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
30
+ self._init_db()
31
+
32
+ def _init_db(self) -> None:
33
+ """Initialize SQLite database"""
34
+ conn = sqlite3.connect(self.db_path)
35
+ cursor = conn.cursor()
36
+
37
+ cursor.execute(
38
+ """
39
+ CREATE TABLE IF NOT EXISTS alerts (
40
+ id TEXT PRIMARY KEY,
41
+ name TEXT NOT NULL,
42
+ metric TEXT NOT NULL,
43
+ threshold REAL NOT NULL,
44
+ channel TEXT NOT NULL,
45
+ webhook_url TEXT,
46
+ email TEXT,
47
+ enabled INTEGER DEFAULT 1,
48
+ cooldown INTEGER DEFAULT 3600,
49
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
50
+ )
51
+ """
52
+ )
53
+
54
+ conn.commit()
55
+ conn.close()
56
+
57
+ def add_alert(
58
+ self,
59
+ alert_id: str,
60
+ name: str,
61
+ metric: str,
62
+ threshold: float,
63
+ channel: str,
64
+ webhook_url: str | None = None,
65
+ email: str | None = None,
66
+ ) -> None:
67
+ """Add a new alert"""
68
+ conn = sqlite3.connect(self.db_path)
69
+ cursor = conn.cursor()
70
+
71
+ cursor.execute(
72
+ """
73
+ INSERT INTO alerts (id, name, metric, threshold, channel, webhook_url, email)
74
+ VALUES (?, ?, ?, ?, ?, ?, ?)
75
+ """,
76
+ (alert_id, name, metric, threshold, channel, webhook_url, email),
77
+ )
78
+
79
+ conn.commit()
80
+ conn.close()
81
+
82
+ def list_alerts(self) -> list[dict[str, Any]]:
83
+ """List all alerts"""
84
+ conn = sqlite3.connect(self.db_path)
85
+ cursor = conn.cursor()
86
+
87
+ cursor.execute("SELECT * FROM alerts")
88
+ rows = cursor.fetchall()
89
+
90
+ conn.close()
91
+
92
+ alerts = []
93
+ for row in rows:
94
+ alerts.append(
95
+ {
96
+ "id": row[0],
97
+ "name": row[1],
98
+ "metric": row[2],
99
+ "threshold": row[3],
100
+ "channel": row[4],
101
+ "webhook_url": row[5],
102
+ "email": row[6],
103
+ "enabled": bool(row[7]),
104
+ "cooldown": row[8],
105
+ "created_at": row[9],
106
+ }
107
+ )
108
+
109
+ return alerts
110
+
111
+ def delete_alert(self, alert_id: str) -> bool:
112
+ """Delete an alert by ID"""
113
+ conn = sqlite3.connect(self.db_path)
114
+ cursor = conn.cursor()
115
+
116
+ cursor.execute("DELETE FROM alerts WHERE id = ?", (alert_id,))
117
+ deleted = cursor.rowcount > 0
118
+
119
+ conn.commit()
120
+ conn.close()
121
+
122
+ return deleted
123
+
124
+
125
+ @click.group()
126
+ def alerts():
127
+ """Alert management commands"""
128
+ pass
129
+
130
+
131
+ @alerts.command()
132
+ def init():
133
+ """Initialize alert with interactive wizard"""
134
+ click.echo("🔔 Alert Setup Wizard\n")
135
+
136
+ # Question 1: What metric?
137
+ click.echo("1. What metric do you want to monitor?")
138
+ click.echo(" a) Daily cost")
139
+ click.echo(" b) Error rate")
140
+ click.echo(" c) Latency (avg response time)")
141
+ click.echo(" d) Token usage")
142
+
143
+ metric_choice = click.prompt("Choose (a/b/c/d)", type=click.Choice(["a", "b", "c", "d"]))
144
+
145
+ metric_map = {
146
+ "a": ("daily_cost", "Daily Cost"),
147
+ "b": ("error_rate", "Error Rate"),
148
+ "c": ("avg_latency", "Average Latency"),
149
+ "d": ("token_usage", "Token Usage"),
150
+ }
151
+
152
+ metric, metric_name = metric_map[metric_choice]
153
+
154
+ # Question 2: What threshold?
155
+ click.echo(f"\n2. What threshold for {metric_name}?")
156
+ if metric == "daily_cost":
157
+ threshold = click.prompt("Daily cost threshold (USD)", type=float, default=10.0)
158
+ elif metric == "error_rate":
159
+ threshold = click.prompt("Error rate threshold (%)", type=float, default=10.0)
160
+ elif metric == "avg_latency":
161
+ threshold = click.prompt("Latency threshold (ms)", type=int, default=3000)
162
+ else: # token_usage
163
+ threshold = click.prompt("Token usage threshold", type=int, default=100000)
164
+
165
+ # Question 3: Where to send?
166
+ click.echo("\n3. Where should alerts be sent?")
167
+ click.echo(" a) Webhook (Slack, Discord, etc.)")
168
+ click.echo(" b) Email")
169
+ click.echo(" c) VSCode output (console)")
170
+
171
+ channel_choice = click.prompt("Choose (a/b/c)", type=click.Choice(["a", "b", "c"]))
172
+
173
+ channel_map = {
174
+ "a": "webhook",
175
+ "b": "email",
176
+ "c": "vscode_output",
177
+ }
178
+
179
+ channel = channel_map[channel_choice]
180
+
181
+ webhook_url = None
182
+ email = None
183
+
184
+ if channel == "webhook":
185
+ webhook_url = click.prompt("Webhook URL")
186
+ elif channel == "email":
187
+ email = click.prompt("Email address")
188
+
189
+ # Create alert
190
+ engine = AlertEngine()
191
+ alert_id = f"alert_{metric}_{int(__import__('time').time())}"
192
+
193
+ engine.add_alert(
194
+ alert_id=alert_id,
195
+ name=f"{metric_name} Alert",
196
+ metric=metric,
197
+ threshold=threshold,
198
+ channel=channel,
199
+ webhook_url=webhook_url,
200
+ email=email,
201
+ )
202
+
203
+ click.echo("\n✅ Alert created successfully!")
204
+ click.echo(f" ID: {alert_id}")
205
+ click.echo(f" Metric: {metric_name}")
206
+ click.echo(f" Threshold: {threshold}")
207
+ click.echo(f" Channel: {channel}")
208
+
209
+ click.echo("\n💡 Tip: Run 'empathy alerts watch' to start monitoring")
210
+
211
+
212
+ @alerts.command(name="list")
213
+ def list_cmd():
214
+ """List all configured alerts"""
215
+ engine = AlertEngine()
216
+ alerts_list = engine.list_alerts()
217
+
218
+ if not alerts_list:
219
+ click.echo("No alerts configured. Run 'empathy alerts init' to create one.")
220
+ return
221
+
222
+ click.echo("📋 Configured Alerts:\n")
223
+
224
+ for alert in alerts_list:
225
+ status = "✓ Enabled" if alert["enabled"] else "✗ Disabled"
226
+ click.echo(f" [{status}] {alert['name']}")
227
+ click.echo(f" ID: {alert['id']}")
228
+ click.echo(f" Metric: {alert['metric']} > {alert['threshold']}")
229
+ click.echo(f" Channel: {alert['channel']}")
230
+ click.echo()
231
+
232
+
233
+ @alerts.command()
234
+ @click.argument("alert_id")
235
+ def delete(alert_id: str):
236
+ """Delete an alert by ID"""
237
+ engine = AlertEngine()
238
+ deleted = engine.delete_alert(alert_id)
239
+
240
+ if deleted:
241
+ click.echo(f"✅ Alert '{alert_id}' deleted successfully")
242
+ else:
243
+ click.echo(f"❌ Alert '{alert_id}' not found")
244
+
245
+
246
+ @alerts.command()
247
+ @click.option("--daemon", is_flag=True, help="Run as background daemon (enterprise)")
248
+ def watch(daemon: bool):
249
+ """Watch telemetry and trigger alerts"""
250
+ if daemon:
251
+ click.echo("🔄 Starting alert watcher as daemon...")
252
+ click.echo("⚠️ Note: Daemon mode is an enterprise feature for 24/7 monitoring")
253
+ click.echo(" For development, use VSCode extension polling instead.")
254
+ # TODO: Implement daemon mode
255
+ else:
256
+ click.echo("🔄 Starting alert watcher (Ctrl+C to stop)...")
257
+ click.echo("💡 Tip: Use VSCode extension for automatic monitoring")
258
+
259
+ try:
260
+ while True:
261
+ # TODO: Check telemetry and trigger alerts
262
+ __import__("time").sleep(60)
263
+ except KeyboardInterrupt:
264
+ click.echo("\n✓ Alert watcher stopped")
265
+
266
+
267
+ if __name__ == "__main__":
268
+ alerts()
@@ -0,0 +1,271 @@
1
+ """Multi-Backend Telemetry Support
2
+
3
+ Enables simultaneous logging to multiple backends (JSONL + OTEL).
4
+
5
+ **Features:**
6
+ - Composite pattern for multiple backends
7
+ - Parallel writes to all configured backends
8
+ - Graceful handling of backend failures
9
+ - Automatic backend selection based on configuration
10
+
11
+ **Example:**
12
+ ```python
13
+ from empathy_os.monitoring import TelemetryStore
14
+ from empathy_os.monitoring.otel_backend import OTELBackend
15
+ from empathy_os.monitoring.multi_backend import MultiBackend
16
+
17
+ # Create composite backend
18
+ backends = [
19
+ TelemetryStore(), # JSONL (always enabled)
20
+ OTELBackend(), # OTEL (if configured)
21
+ ]
22
+ multi = MultiBackend(backends)
23
+
24
+ # Logs to both backends
25
+ multi.log_call(record)
26
+ ```
27
+
28
+ Copyright 2025 Smart-AI-Memory
29
+ Licensed under Fair Source License 0.9
30
+ """
31
+
32
+ from typing import Protocol, runtime_checkable
33
+
34
+ from empathy_os.models.telemetry import LLMCallRecord, TelemetryStore, WorkflowRunRecord
35
+
36
+
37
+ @runtime_checkable
38
+ class TelemetryBackend(Protocol):
39
+ """Protocol for telemetry storage backends.
40
+
41
+ All backends must implement log_call() and log_workflow().
42
+ """
43
+
44
+ def log_call(self, record: LLMCallRecord) -> None:
45
+ """Log an LLM call record."""
46
+ ...
47
+
48
+ def log_workflow(self, record: WorkflowRunRecord) -> None:
49
+ """Log a workflow run record."""
50
+ ...
51
+
52
+
53
+ class MultiBackend:
54
+ """Composite backend for simultaneous logging to multiple backends.
55
+
56
+ Implements the TelemetryBackend protocol and forwards calls to all
57
+ configured backends. Handles failures gracefully - if one backend
58
+ fails, others continue to work.
59
+
60
+ **Auto-Configuration:**
61
+ - JSONL backend is always enabled (default)
62
+ - OTEL backend is enabled if EMPATHY_OTEL_ENDPOINT is set
63
+
64
+ Example:
65
+ >>> backend = MultiBackend.from_config()
66
+ >>> backend.log_call(call_record) # Logs to JSONL + OTEL
67
+ >>> backend.log_workflow(workflow_record)
68
+ """
69
+
70
+ def __init__(self, backends: list[TelemetryBackend] | None = None):
71
+ """Initialize multi-backend.
72
+
73
+ Args:
74
+ backends: List of backend instances (default: auto-detect)
75
+ """
76
+ self.backends = backends or []
77
+ self._failed_backends: set[int] = set()
78
+
79
+ @classmethod
80
+ def from_config(cls, storage_dir: str = ".empathy") -> "MultiBackend":
81
+ """Create multi-backend from configuration.
82
+
83
+ Auto-detects available backends:
84
+ 1. JSONL backend (always enabled)
85
+ 2. OTEL backend (if EMPATHY_OTEL_ENDPOINT is set or collector detected)
86
+
87
+ Args:
88
+ storage_dir: Directory for JSONL storage (default: .empathy)
89
+
90
+ Returns:
91
+ MultiBackend instance with all available backends
92
+ """
93
+ backends: list[TelemetryBackend] = []
94
+
95
+ # Always add JSONL backend
96
+ try:
97
+ jsonl_backend = TelemetryStore(storage_dir)
98
+ backends.append(jsonl_backend)
99
+ except Exception as e:
100
+ print(f"⚠️ Failed to initialize JSONL backend: {e}")
101
+
102
+ # Add OTEL backend if configured
103
+ try:
104
+ from empathy_os.monitoring.otel_backend import OTELBackend
105
+
106
+ otel_backend = OTELBackend()
107
+ if otel_backend.is_available():
108
+ backends.append(otel_backend)
109
+ except ImportError:
110
+ # OTEL dependencies not installed
111
+ pass
112
+ except Exception as e:
113
+ print(f"⚠️ Failed to initialize OTEL backend: {e}")
114
+
115
+ return cls(backends)
116
+
117
+ def add_backend(self, backend: TelemetryBackend) -> None:
118
+ """Add a backend to the multi-backend.
119
+
120
+ Args:
121
+ backend: Backend instance to add
122
+ """
123
+ if isinstance(backend, TelemetryBackend):
124
+ self.backends.append(backend)
125
+ else:
126
+ raise TypeError(
127
+ f"Backend must implement TelemetryBackend protocol, got {type(backend)}"
128
+ )
129
+
130
+ def remove_backend(self, backend: TelemetryBackend) -> None:
131
+ """Remove a backend from the multi-backend.
132
+
133
+ Args:
134
+ backend: Backend instance to remove
135
+ """
136
+ if backend in self.backends:
137
+ self.backends.remove(backend)
138
+
139
+ def log_call(self, record: LLMCallRecord) -> None:
140
+ """Log an LLM call record to all backends.
141
+
142
+ Failures in individual backends are logged but don't affect other backends.
143
+
144
+ Args:
145
+ record: LLM call record to log
146
+ """
147
+ for i, backend in enumerate(self.backends):
148
+ if i in self._failed_backends:
149
+ # Skip backends that have failed before
150
+ continue
151
+
152
+ try:
153
+ backend.log_call(record)
154
+ except Exception as e:
155
+ backend_name = type(backend).__name__
156
+ print(f"⚠️ Failed to log call to {backend_name}: {e}")
157
+ # Mark backend as failed to reduce log spam
158
+ self._failed_backends.add(i)
159
+
160
+ def log_workflow(self, record: WorkflowRunRecord) -> None:
161
+ """Log a workflow run record to all backends.
162
+
163
+ Failures in individual backends are logged but don't affect other backends.
164
+
165
+ Args:
166
+ record: Workflow run record to log
167
+ """
168
+ for i, backend in enumerate(self.backends):
169
+ if i in self._failed_backends:
170
+ # Skip backends that have failed before
171
+ continue
172
+
173
+ try:
174
+ backend.log_workflow(record)
175
+ except Exception as e:
176
+ backend_name = type(backend).__name__
177
+ print(f"⚠️ Failed to log workflow to {backend_name}: {e}")
178
+ # Mark backend as failed to reduce log spam
179
+ self._failed_backends.add(i)
180
+
181
+ def get_active_backends(self) -> list[str]:
182
+ """Get list of active backend names.
183
+
184
+ Returns:
185
+ List of backend class names that are active (not failed)
186
+ """
187
+ return [
188
+ type(backend).__name__
189
+ for i, backend in enumerate(self.backends)
190
+ if i not in self._failed_backends
191
+ ]
192
+
193
+ def get_failed_backends(self) -> list[str]:
194
+ """Get list of failed backend names.
195
+
196
+ Returns:
197
+ List of backend class names that have failed
198
+ """
199
+ return [
200
+ type(self.backends[i]).__name__ for i in self._failed_backends if i < len(self.backends)
201
+ ]
202
+
203
+ def reset_failures(self) -> None:
204
+ """Reset failed backend tracking.
205
+
206
+ Allows retry of previously failed backends.
207
+ """
208
+ self._failed_backends.clear()
209
+
210
+ def flush(self) -> None:
211
+ """Flush all backends.
212
+
213
+ Calls flush() on backends that support it (e.g., OTEL backend).
214
+ """
215
+ for backend in self.backends:
216
+ if hasattr(backend, "flush"):
217
+ try:
218
+ backend.flush()
219
+ except Exception as e:
220
+ backend_name = type(backend).__name__
221
+ print(f"⚠️ Failed to flush {backend_name}: {e}")
222
+
223
+ def __len__(self) -> int:
224
+ """Return number of active backends."""
225
+ return len(self.backends) - len(self._failed_backends)
226
+
227
+ def __repr__(self) -> str:
228
+ """String representation."""
229
+ active = self.get_active_backends()
230
+ failed = self.get_failed_backends()
231
+ status = f"active={active}"
232
+ if failed:
233
+ status += f", failed={failed}"
234
+ return f"MultiBackend({status})"
235
+
236
+
237
+ # Singleton instance for global access
238
+ _global_backend: MultiBackend | None = None
239
+
240
+
241
+ def get_multi_backend(storage_dir: str = ".empathy") -> MultiBackend:
242
+ """Get or create the global multi-backend instance.
243
+
244
+ This is the recommended way to get a multi-backend instance.
245
+ It ensures a single instance is shared across the application.
246
+
247
+ Args:
248
+ storage_dir: Directory for JSONL storage (default: .empathy)
249
+
250
+ Returns:
251
+ Global MultiBackend instance
252
+
253
+ Example:
254
+ >>> backend = get_multi_backend()
255
+ >>> backend.log_call(record)
256
+ """
257
+ global _global_backend
258
+ if _global_backend is None:
259
+ _global_backend = MultiBackend.from_config(storage_dir)
260
+ return _global_backend
261
+
262
+
263
+ def reset_multi_backend() -> None:
264
+ """Reset the global multi-backend instance.
265
+
266
+ Useful for testing or reconfiguration.
267
+ """
268
+ global _global_backend
269
+ if _global_backend is not None:
270
+ _global_backend.flush()
271
+ _global_backend = None