empathy-framework 2.4.0__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.
- coach_wizards/__init__.py +13 -12
- coach_wizards/accessibility_wizard.py +12 -12
- coach_wizards/api_wizard.py +12 -12
- coach_wizards/base_wizard.py +26 -20
- coach_wizards/cicd_wizard.py +15 -13
- coach_wizards/code_reviewer_README.md +60 -0
- coach_wizards/code_reviewer_wizard.py +180 -0
- coach_wizards/compliance_wizard.py +12 -12
- coach_wizards/database_wizard.py +12 -12
- coach_wizards/debugging_wizard.py +12 -12
- coach_wizards/documentation_wizard.py +12 -12
- coach_wizards/generate_wizards.py +1 -2
- coach_wizards/localization_wizard.py +101 -19
- coach_wizards/migration_wizard.py +12 -12
- coach_wizards/monitoring_wizard.py +12 -12
- coach_wizards/observability_wizard.py +12 -12
- coach_wizards/performance_wizard.py +12 -12
- coach_wizards/prompt_engineering_wizard.py +661 -0
- coach_wizards/refactoring_wizard.py +12 -12
- coach_wizards/scaling_wizard.py +12 -12
- coach_wizards/security_wizard.py +12 -12
- coach_wizards/testing_wizard.py +12 -12
- empathy_framework-3.8.2.dist-info/METADATA +1176 -0
- empathy_framework-3.8.2.dist-info/RECORD +333 -0
- empathy_framework-3.8.2.dist-info/entry_points.txt +22 -0
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/top_level.txt +5 -1
- empathy_healthcare_plugin/__init__.py +1 -2
- empathy_healthcare_plugin/monitors/__init__.py +9 -0
- empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
- empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
- empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
- empathy_llm_toolkit/__init__.py +7 -7
- empathy_llm_toolkit/agent_factory/__init__.py +53 -0
- empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
- empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
- empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
- empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
- empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
- empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
- empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
- empathy_llm_toolkit/agent_factory/base.py +305 -0
- empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
- empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
- empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
- empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
- empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
- empathy_llm_toolkit/agent_factory/decorators.py +286 -0
- empathy_llm_toolkit/agent_factory/factory.py +558 -0
- empathy_llm_toolkit/agent_factory/framework.py +192 -0
- empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
- empathy_llm_toolkit/agent_factory/resilient.py +320 -0
- empathy_llm_toolkit/claude_memory.py +14 -15
- empathy_llm_toolkit/cli/__init__.py +8 -0
- empathy_llm_toolkit/cli/sync_claude.py +487 -0
- empathy_llm_toolkit/code_health.py +186 -28
- empathy_llm_toolkit/config/__init__.py +29 -0
- empathy_llm_toolkit/config/unified.py +295 -0
- empathy_llm_toolkit/contextual_patterns.py +11 -12
- empathy_llm_toolkit/core.py +168 -53
- empathy_llm_toolkit/git_pattern_extractor.py +17 -13
- empathy_llm_toolkit/levels.py +6 -13
- empathy_llm_toolkit/pattern_confidence.py +14 -18
- empathy_llm_toolkit/pattern_resolver.py +10 -12
- empathy_llm_toolkit/pattern_summary.py +16 -14
- empathy_llm_toolkit/providers.py +194 -28
- empathy_llm_toolkit/routing/__init__.py +32 -0
- empathy_llm_toolkit/routing/model_router.py +362 -0
- empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
- empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
- empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
- empathy_llm_toolkit/security/README.md +262 -0
- empathy_llm_toolkit/security/__init__.py +62 -0
- empathy_llm_toolkit/security/audit_logger.py +929 -0
- empathy_llm_toolkit/security/audit_logger_example.py +152 -0
- empathy_llm_toolkit/security/pii_scrubber.py +640 -0
- empathy_llm_toolkit/security/secrets_detector.py +678 -0
- empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
- empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
- empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
- empathy_llm_toolkit/session_status.py +20 -22
- empathy_llm_toolkit/state.py +28 -21
- empathy_llm_toolkit/wizards/__init__.py +38 -0
- empathy_llm_toolkit/wizards/base_wizard.py +364 -0
- empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
- empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
- empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
- empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
- empathy_os/__init__.py +125 -84
- empathy_os/adaptive/__init__.py +13 -0
- empathy_os/adaptive/task_complexity.py +127 -0
- empathy_os/{monitoring.py → agent_monitoring.py} +28 -28
- empathy_os/cache/__init__.py +117 -0
- empathy_os/cache/base.py +166 -0
- empathy_os/cache/dependency_manager.py +253 -0
- empathy_os/cache/hash_only.py +248 -0
- empathy_os/cache/hybrid.py +390 -0
- empathy_os/cache/storage.py +282 -0
- empathy_os/cli.py +1516 -70
- empathy_os/cli_unified.py +597 -0
- empathy_os/config/__init__.py +63 -0
- empathy_os/config/xml_config.py +239 -0
- empathy_os/config.py +95 -37
- empathy_os/coordination.py +72 -68
- empathy_os/core.py +94 -107
- empathy_os/cost_tracker.py +74 -55
- empathy_os/dashboard/__init__.py +15 -0
- empathy_os/dashboard/server.py +743 -0
- empathy_os/discovery.py +17 -14
- empathy_os/emergence.py +21 -22
- empathy_os/exceptions.py +18 -30
- empathy_os/feedback_loops.py +30 -33
- empathy_os/levels.py +32 -35
- empathy_os/leverage_points.py +31 -32
- empathy_os/logging_config.py +19 -16
- empathy_os/memory/__init__.py +195 -0
- empathy_os/memory/claude_memory.py +466 -0
- empathy_os/memory/config.py +224 -0
- empathy_os/memory/control_panel.py +1298 -0
- empathy_os/memory/edges.py +179 -0
- empathy_os/memory/graph.py +567 -0
- empathy_os/memory/long_term.py +1194 -0
- empathy_os/memory/nodes.py +179 -0
- empathy_os/memory/redis_bootstrap.py +540 -0
- empathy_os/memory/security/__init__.py +31 -0
- empathy_os/memory/security/audit_logger.py +930 -0
- empathy_os/memory/security/pii_scrubber.py +640 -0
- empathy_os/memory/security/secrets_detector.py +678 -0
- empathy_os/memory/short_term.py +2119 -0
- empathy_os/memory/storage/__init__.py +15 -0
- empathy_os/memory/summary_index.py +583 -0
- empathy_os/memory/unified.py +619 -0
- empathy_os/metrics/__init__.py +12 -0
- empathy_os/metrics/prompt_metrics.py +190 -0
- empathy_os/models/__init__.py +136 -0
- empathy_os/models/__main__.py +13 -0
- empathy_os/models/cli.py +655 -0
- empathy_os/models/empathy_executor.py +354 -0
- empathy_os/models/executor.py +252 -0
- empathy_os/models/fallback.py +671 -0
- empathy_os/models/provider_config.py +563 -0
- empathy_os/models/registry.py +382 -0
- empathy_os/models/tasks.py +302 -0
- empathy_os/models/telemetry.py +548 -0
- empathy_os/models/token_estimator.py +378 -0
- empathy_os/models/validation.py +274 -0
- empathy_os/monitoring/__init__.py +52 -0
- empathy_os/monitoring/alerts.py +23 -0
- empathy_os/monitoring/alerts_cli.py +268 -0
- empathy_os/monitoring/multi_backend.py +271 -0
- empathy_os/monitoring/otel_backend.py +363 -0
- empathy_os/optimization/__init__.py +19 -0
- empathy_os/optimization/context_optimizer.py +272 -0
- empathy_os/pattern_library.py +30 -29
- empathy_os/persistence.py +35 -37
- empathy_os/platform_utils.py +261 -0
- empathy_os/plugins/__init__.py +28 -0
- empathy_os/plugins/base.py +361 -0
- empathy_os/plugins/registry.py +268 -0
- empathy_os/project_index/__init__.py +30 -0
- empathy_os/project_index/cli.py +335 -0
- empathy_os/project_index/crew_integration.py +430 -0
- empathy_os/project_index/index.py +425 -0
- empathy_os/project_index/models.py +501 -0
- empathy_os/project_index/reports.py +473 -0
- empathy_os/project_index/scanner.py +538 -0
- empathy_os/prompts/__init__.py +61 -0
- empathy_os/prompts/config.py +77 -0
- empathy_os/prompts/context.py +177 -0
- empathy_os/prompts/parser.py +285 -0
- empathy_os/prompts/registry.py +313 -0
- empathy_os/prompts/templates.py +208 -0
- empathy_os/redis_config.py +144 -58
- empathy_os/redis_memory.py +79 -77
- empathy_os/resilience/__init__.py +56 -0
- empathy_os/resilience/circuit_breaker.py +256 -0
- empathy_os/resilience/fallback.py +179 -0
- empathy_os/resilience/health.py +300 -0
- empathy_os/resilience/retry.py +209 -0
- empathy_os/resilience/timeout.py +135 -0
- empathy_os/routing/__init__.py +43 -0
- empathy_os/routing/chain_executor.py +433 -0
- empathy_os/routing/classifier.py +217 -0
- empathy_os/routing/smart_router.py +234 -0
- empathy_os/routing/wizard_registry.py +307 -0
- empathy_os/templates.py +19 -14
- empathy_os/trust/__init__.py +28 -0
- empathy_os/trust/circuit_breaker.py +579 -0
- empathy_os/trust_building.py +67 -58
- empathy_os/validation/__init__.py +19 -0
- empathy_os/validation/xml_validator.py +281 -0
- empathy_os/wizard_factory_cli.py +170 -0
- empathy_os/{workflows.py → workflow_commands.py} +131 -37
- empathy_os/workflows/__init__.py +360 -0
- empathy_os/workflows/base.py +1660 -0
- empathy_os/workflows/bug_predict.py +962 -0
- empathy_os/workflows/code_review.py +960 -0
- empathy_os/workflows/code_review_adapters.py +310 -0
- empathy_os/workflows/code_review_pipeline.py +720 -0
- empathy_os/workflows/config.py +600 -0
- empathy_os/workflows/dependency_check.py +648 -0
- empathy_os/workflows/document_gen.py +1069 -0
- empathy_os/workflows/documentation_orchestrator.py +1205 -0
- empathy_os/workflows/health_check.py +679 -0
- empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
- empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
- empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
- empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
- empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
- empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
- empathy_os/workflows/manage_documentation.py +804 -0
- empathy_os/workflows/new_sample_workflow1.py +146 -0
- empathy_os/workflows/new_sample_workflow1_README.md +150 -0
- empathy_os/workflows/perf_audit.py +687 -0
- empathy_os/workflows/pr_review.py +748 -0
- empathy_os/workflows/progress.py +445 -0
- empathy_os/workflows/progress_server.py +322 -0
- empathy_os/workflows/refactor_plan.py +693 -0
- empathy_os/workflows/release_prep.py +808 -0
- empathy_os/workflows/research_synthesis.py +404 -0
- empathy_os/workflows/secure_release.py +585 -0
- empathy_os/workflows/security_adapters.py +297 -0
- empathy_os/workflows/security_audit.py +1046 -0
- empathy_os/workflows/step_config.py +234 -0
- empathy_os/workflows/test5.py +125 -0
- empathy_os/workflows/test5_README.md +158 -0
- empathy_os/workflows/test_gen.py +1855 -0
- empathy_os/workflows/test_lifecycle.py +526 -0
- empathy_os/workflows/test_maintenance.py +626 -0
- empathy_os/workflows/test_maintenance_cli.py +590 -0
- empathy_os/workflows/test_maintenance_crew.py +821 -0
- empathy_os/workflows/xml_enhanced_crew.py +285 -0
- empathy_software_plugin/__init__.py +1 -2
- empathy_software_plugin/cli/__init__.py +120 -0
- empathy_software_plugin/cli/inspect.py +362 -0
- empathy_software_plugin/cli.py +49 -27
- empathy_software_plugin/plugin.py +4 -8
- empathy_software_plugin/wizards/__init__.py +42 -0
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
- empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
- empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
- empathy_software_plugin/wizards/base_wizard.py +288 -0
- empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
- empathy_software_plugin/wizards/code_review_wizard.py +606 -0
- empathy_software_plugin/wizards/debugging/__init__.py +50 -0
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
- empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
- empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
- empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
- empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
- empathy_software_plugin/wizards/debugging/verification.py +369 -0
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
- empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
- empathy_software_plugin/wizards/performance/__init__.py +9 -0
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
- empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
- empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
- empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
- empathy_software_plugin/wizards/security/__init__.py +32 -0
- empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
- empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
- empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
- empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
- empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
- empathy_software_plugin/wizards/testing/__init__.py +27 -0
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
- empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
- empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
- empathy_software_plugin/wizards/testing_wizard.py +274 -0
- hot_reload/README.md +473 -0
- hot_reload/__init__.py +62 -0
- hot_reload/config.py +84 -0
- hot_reload/integration.py +228 -0
- hot_reload/reloader.py +298 -0
- hot_reload/watcher.py +179 -0
- hot_reload/websocket.py +176 -0
- scaffolding/README.md +589 -0
- scaffolding/__init__.py +35 -0
- scaffolding/__main__.py +14 -0
- scaffolding/cli.py +240 -0
- test_generator/__init__.py +38 -0
- test_generator/__main__.py +14 -0
- test_generator/cli.py +226 -0
- test_generator/generator.py +325 -0
- test_generator/risk_analyzer.py +216 -0
- workflow_patterns/__init__.py +33 -0
- workflow_patterns/behavior.py +249 -0
- workflow_patterns/core.py +76 -0
- workflow_patterns/output.py +99 -0
- workflow_patterns/registry.py +255 -0
- workflow_patterns/structural.py +288 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
- agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
- agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
- agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
- agents/compliance_anticipation_agent.py +0 -1427
- agents/epic_integration_wizard.py +0 -541
- agents/trust_building_behaviors.py +0 -891
- empathy_framework-2.4.0.dist-info/METADATA +0 -485
- empathy_framework-2.4.0.dist-info/RECORD +0 -102
- empathy_framework-2.4.0.dist-info/entry_points.txt +0 -6
- empathy_llm_toolkit/htmlcov/status.json +0 -1
- empathy_llm_toolkit/security/htmlcov/status.json +0 -1
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/WHEEL +0 -0
- {empathy_framework-2.4.0.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
|