empathy-framework 3.5.6__py3-none-any.whl → 3.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. agents/compliance_anticipation_agent.py +113 -118
  2. agents/compliance_db.py +339 -0
  3. agents/epic_integration_wizard.py +37 -48
  4. agents/notifications.py +291 -0
  5. agents/trust_building_behaviors.py +66 -85
  6. coach_wizards/__init__.py +11 -12
  7. coach_wizards/accessibility_wizard.py +12 -12
  8. coach_wizards/api_wizard.py +12 -12
  9. coach_wizards/base_wizard.py +26 -20
  10. coach_wizards/cicd_wizard.py +15 -13
  11. coach_wizards/compliance_wizard.py +12 -12
  12. coach_wizards/database_wizard.py +12 -12
  13. coach_wizards/debugging_wizard.py +12 -12
  14. coach_wizards/documentation_wizard.py +12 -12
  15. coach_wizards/generate_wizards.py +1 -2
  16. coach_wizards/localization_wizard.py +21 -14
  17. coach_wizards/migration_wizard.py +12 -12
  18. coach_wizards/monitoring_wizard.py +12 -12
  19. coach_wizards/observability_wizard.py +12 -12
  20. coach_wizards/performance_wizard.py +12 -12
  21. coach_wizards/prompt_engineering_wizard.py +22 -25
  22. coach_wizards/refactoring_wizard.py +12 -12
  23. coach_wizards/scaling_wizard.py +12 -12
  24. coach_wizards/security_wizard.py +12 -12
  25. coach_wizards/testing_wizard.py +12 -12
  26. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/METADATA +234 -30
  27. empathy_framework-3.7.0.dist-info/RECORD +105 -0
  28. empathy_healthcare_plugin/__init__.py +1 -2
  29. empathy_llm_toolkit/__init__.py +5 -6
  30. empathy_llm_toolkit/claude_memory.py +14 -15
  31. empathy_llm_toolkit/code_health.py +27 -19
  32. empathy_llm_toolkit/contextual_patterns.py +11 -12
  33. empathy_llm_toolkit/core.py +43 -49
  34. empathy_llm_toolkit/git_pattern_extractor.py +16 -12
  35. empathy_llm_toolkit/levels.py +6 -13
  36. empathy_llm_toolkit/pattern_confidence.py +14 -18
  37. empathy_llm_toolkit/pattern_resolver.py +10 -12
  38. empathy_llm_toolkit/pattern_summary.py +13 -11
  39. empathy_llm_toolkit/providers.py +27 -38
  40. empathy_llm_toolkit/session_status.py +18 -20
  41. empathy_llm_toolkit/state.py +20 -21
  42. empathy_os/__init__.py +72 -73
  43. empathy_os/cli.py +193 -98
  44. empathy_os/cli_unified.py +68 -41
  45. empathy_os/config.py +31 -31
  46. empathy_os/coordination.py +48 -54
  47. empathy_os/core.py +90 -99
  48. empathy_os/cost_tracker.py +20 -23
  49. empathy_os/discovery.py +9 -11
  50. empathy_os/emergence.py +20 -21
  51. empathy_os/exceptions.py +18 -30
  52. empathy_os/feedback_loops.py +27 -30
  53. empathy_os/levels.py +31 -34
  54. empathy_os/leverage_points.py +27 -28
  55. empathy_os/logging_config.py +11 -12
  56. empathy_os/monitoring.py +27 -27
  57. empathy_os/pattern_library.py +29 -28
  58. empathy_os/persistence.py +30 -34
  59. empathy_os/platform_utils.py +46 -47
  60. empathy_os/redis_config.py +14 -15
  61. empathy_os/redis_memory.py +53 -56
  62. empathy_os/templates.py +12 -11
  63. empathy_os/trust_building.py +44 -36
  64. empathy_os/workflow_commands.py +123 -31
  65. empathy_software_plugin/__init__.py +1 -2
  66. empathy_software_plugin/cli.py +32 -25
  67. empathy_software_plugin/plugin.py +4 -8
  68. empathy_framework-3.5.6.dist-info/RECORD +0 -103
  69. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/WHEEL +0 -0
  70. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/entry_points.txt +0 -0
  71. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/licenses/LICENSE +0 -0
  72. {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,291 @@
1
+ """Notification system for compliance alerts.
2
+
3
+ Supports multiple channels: Email (SMTP), Slack webhooks, SMS (Twilio).
4
+ Graceful fallback when notification delivery fails.
5
+
6
+ Copyright 2025 Smart-AI-Memory
7
+ Licensed under Fair Source License 0.9
8
+ """
9
+
10
+ import logging
11
+ import os
12
+ import smtplib
13
+ from dataclasses import dataclass
14
+ from email.mime.multipart import MIMEMultipart
15
+ from email.mime.text import MIMEText
16
+ from typing import Any
17
+
18
+ import requests
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @dataclass
24
+ class NotificationConfig:
25
+ """Configuration for notification channels."""
26
+
27
+ # Email (SMTP)
28
+ smtp_host: str | None = None
29
+ smtp_port: int = 587
30
+ smtp_user: str | None = None
31
+ smtp_password: str | None = None
32
+ smtp_from: str | None = None
33
+
34
+ # Slack
35
+ slack_webhook_url: str | None = None
36
+
37
+ # Twilio SMS
38
+ twilio_account_sid: str | None = None
39
+ twilio_auth_token: str | None = None
40
+ twilio_from_number: str | None = None
41
+
42
+ @classmethod
43
+ def from_env(cls) -> "NotificationConfig":
44
+ """Load configuration from environment variables."""
45
+ return cls(
46
+ # Email
47
+ smtp_host=os.getenv("SMTP_HOST"),
48
+ smtp_port=int(os.getenv("SMTP_PORT", "587")),
49
+ smtp_user=os.getenv("SMTP_USER"),
50
+ smtp_password=os.getenv("SMTP_PASSWORD"),
51
+ smtp_from=os.getenv("SMTP_FROM"),
52
+ # Slack
53
+ slack_webhook_url=os.getenv("SLACK_WEBHOOK_URL"),
54
+ # Twilio
55
+ twilio_account_sid=os.getenv("TWILIO_ACCOUNT_SID"),
56
+ twilio_auth_token=os.getenv("TWILIO_AUTH_TOKEN"),
57
+ twilio_from_number=os.getenv("TWILIO_FROM_NUMBER"),
58
+ )
59
+
60
+
61
+ class NotificationService:
62
+ """Multi-channel notification service with graceful fallback."""
63
+
64
+ def __init__(self, config: NotificationConfig | None = None):
65
+ """Initialize notification service.
66
+
67
+ Args:
68
+ config: Notification configuration (loads from env if None)
69
+ """
70
+ self.config = config or NotificationConfig.from_env()
71
+
72
+ def send_email(
73
+ self,
74
+ to: list[str],
75
+ subject: str,
76
+ body: str,
77
+ html_body: str | None = None,
78
+ ) -> bool:
79
+ """Send email notification via SMTP.
80
+
81
+ Args:
82
+ to: List of recipient email addresses
83
+ subject: Email subject
84
+ body: Plain text body
85
+ html_body: Optional HTML body
86
+
87
+ Returns:
88
+ True if sent successfully, False otherwise
89
+ """
90
+ if not self.config.smtp_host:
91
+ logger.warning("SMTP not configured, skipping email notification")
92
+ return False
93
+
94
+ try:
95
+ # Create message
96
+ msg = MIMEMultipart("alternative")
97
+ msg["Subject"] = subject
98
+ msg["From"] = self.config.smtp_from or "noreply@smartaimemory.com"
99
+ msg["To"] = ", ".join(to)
100
+
101
+ # Attach plain text and HTML
102
+ msg.attach(MIMEText(body, "plain"))
103
+ if html_body:
104
+ msg.attach(MIMEText(html_body, "html"))
105
+
106
+ # Send via SMTP
107
+ with smtplib.SMTP(self.config.smtp_host, self.config.smtp_port) as server:
108
+ server.starttls()
109
+ if self.config.smtp_user and self.config.smtp_password:
110
+ server.login(self.config.smtp_user, self.config.smtp_password)
111
+ server.send_message(msg)
112
+
113
+ logger.info(f"Email sent to {len(to)} recipients: {subject}")
114
+ return True
115
+
116
+ except Exception as e:
117
+ logger.error(f"Failed to send email: {e}")
118
+ return False
119
+
120
+ def send_slack(
121
+ self,
122
+ message: str,
123
+ channel: str | None = None,
124
+ blocks: list[dict[str, Any]] | None = None,
125
+ ) -> bool:
126
+ """Send Slack notification via webhook.
127
+
128
+ Args:
129
+ message: Message text (fallback for blocks)
130
+ channel: Optional channel override
131
+ blocks: Optional Slack blocks for rich formatting
132
+
133
+ Returns:
134
+ True if sent successfully, False otherwise
135
+ """
136
+ if not self.config.slack_webhook_url:
137
+ logger.warning("Slack webhook not configured, skipping notification")
138
+ return False
139
+
140
+ try:
141
+ payload: dict[str, Any] = {"text": message}
142
+
143
+ if channel:
144
+ payload["channel"] = channel
145
+
146
+ if blocks:
147
+ payload["blocks"] = blocks
148
+
149
+ response = requests.post(
150
+ self.config.slack_webhook_url,
151
+ json=payload,
152
+ headers={"Content-Type": "application/json"},
153
+ timeout=10,
154
+ )
155
+
156
+ if response.status_code == 200:
157
+ logger.info(f"Slack notification sent: {message[:50]}...")
158
+ return True
159
+ else:
160
+ logger.error(f"Slack webhook failed: {response.status_code} {response.text}")
161
+ return False
162
+
163
+ except Exception as e:
164
+ logger.error(f"Failed to send Slack notification: {e}")
165
+ return False
166
+
167
+ def send_sms(self, to: str, message: str) -> bool:
168
+ """Send SMS notification via Twilio.
169
+
170
+ Args:
171
+ to: Phone number (E.164 format: +1234567890)
172
+ message: SMS message text (max 160 chars recommended)
173
+
174
+ Returns:
175
+ True if sent successfully, False otherwise
176
+ """
177
+ if not all([self.config.twilio_account_sid, self.config.twilio_auth_token]):
178
+ logger.warning("Twilio not configured, skipping SMS notification")
179
+ return False
180
+
181
+ try:
182
+ # Import Twilio client (optional dependency)
183
+ from twilio.rest import Client
184
+
185
+ client = Client(self.config.twilio_account_sid, self.config.twilio_auth_token)
186
+
187
+ client.messages.create(
188
+ to=to,
189
+ from_=self.config.twilio_from_number,
190
+ body=message,
191
+ )
192
+
193
+ logger.info(f"SMS sent to {to}")
194
+ return True
195
+
196
+ except ImportError:
197
+ logger.error("Twilio SDK not installed. Install with: pip install twilio")
198
+ return False
199
+ except Exception as e:
200
+ logger.error(f"Failed to send SMS: {e}")
201
+ return False
202
+
203
+ def send_compliance_alert(
204
+ self,
205
+ severity: str,
206
+ title: str,
207
+ description: str,
208
+ recipients: dict[str, list[str]], # {'email': [...], 'phone': [...]}
209
+ ) -> dict[str, bool]:
210
+ """Send multi-channel compliance alert.
211
+
212
+ Args:
213
+ severity: Alert severity ('critical', 'high', 'medium', 'low')
214
+ title: Alert title
215
+ description: Alert description
216
+ recipients: Dict with 'email' and 'phone' lists
217
+
218
+ Returns:
219
+ Dict with channel success status: {'email': True, 'slack': True, 'sms': False}
220
+ """
221
+ results = {}
222
+
223
+ # Email alert
224
+ if recipients.get("email"):
225
+ email_subject = f"[{severity.upper()}] Compliance Alert: {title}"
226
+ email_body = f"""
227
+ Compliance Alert
228
+ ================
229
+
230
+ Severity: {severity.upper()}
231
+ Title: {title}
232
+
233
+ Description:
234
+ {description}
235
+
236
+ ---
237
+ Generated by Empathy Framework Compliance Monitor
238
+ """.strip()
239
+
240
+ results["email"] = self.send_email(
241
+ to=recipients["email"],
242
+ subject=email_subject,
243
+ body=email_body,
244
+ )
245
+
246
+ # Slack alert
247
+ slack_emoji = {
248
+ "critical": ":rotating_light:",
249
+ "high": ":warning:",
250
+ "medium": ":grey_exclamation:",
251
+ "low": ":information_source:",
252
+ }.get(severity, ":bell:")
253
+
254
+ slack_message = f"{slack_emoji} *[{severity.upper()}] {title}*\n\n{description}"
255
+
256
+ slack_blocks = [
257
+ {
258
+ "type": "header",
259
+ "text": {
260
+ "type": "plain_text",
261
+ "text": f"{slack_emoji} Compliance Alert",
262
+ },
263
+ },
264
+ {
265
+ "type": "section",
266
+ "fields": [
267
+ {"type": "mrkdwn", "text": f"*Severity:*\n{severity.upper()}"},
268
+ {"type": "mrkdwn", "text": f"*Title:*\n{title}"},
269
+ ],
270
+ },
271
+ {
272
+ "type": "section",
273
+ "text": {"type": "mrkdwn", "text": f"*Description:*\n{description}"},
274
+ },
275
+ ]
276
+
277
+ results["slack"] = self.send_slack(slack_message, blocks=slack_blocks)
278
+
279
+ # SMS alert (only for critical/high)
280
+ if severity in ["critical", "high"] and recipients.get("phone"):
281
+ sms_message = f"[{severity.upper()}] {title}: {description[:100]}"
282
+
283
+ # Send to all phone numbers
284
+ sms_results = []
285
+ for phone in recipients["phone"]:
286
+ sms_results.append(self.send_sms(phone, sms_message))
287
+
288
+ results["sms"] = any(sms_results) # Success if any SMS sent
289
+
290
+ logger.info(f"Compliance alert sent: {results}")
291
+ return results