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
empathy_os/cli_unified.py CHANGED
@@ -1,5 +1,4 @@
1
- """
2
- Unified CLI for Empathy Framework
1
+ """Unified CLI for Empathy Framework
3
2
 
4
3
  A single entry point for all Empathy Framework commands using Typer.
5
4
 
@@ -60,8 +59,7 @@ def callback(
60
59
  help="Show version and exit",
61
60
  ),
62
61
  ):
63
- """
64
- Empathy Framework - Predictive AI-Developer Collaboration
62
+ """Empathy Framework - Predictive AI-Developer Collaboration
65
63
 
66
64
  The AI collaboration framework that predicts problems before they happen.
67
65
 
@@ -82,7 +80,6 @@ def callback(
82
80
  empathy scan . Scan codebase for issues
83
81
  empathy inspect . Deep inspection with SARIF output
84
82
  """
85
- pass
86
83
 
87
84
 
88
85
  # =============================================================================
@@ -97,31 +94,33 @@ app.add_typer(memory_app, name="memory")
97
94
  def memory_status():
98
95
  """Check memory system status (Redis, patterns, stats)."""
99
96
  # Delegate to the existing CLI
100
- subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "status"])
97
+ subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "status"], check=False)
101
98
 
102
99
 
103
100
  @memory_app.command("start")
104
101
  def memory_start():
105
102
  """Start Redis server for short-term memory."""
106
- subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "start"])
103
+ subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "start"], check=False)
107
104
 
108
105
 
109
106
  @memory_app.command("stop")
110
107
  def memory_stop():
111
108
  """Stop Redis server."""
112
- subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "stop"])
109
+ subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "stop"], check=False)
113
110
 
114
111
 
115
112
  @memory_app.command("stats")
116
113
  def memory_stats():
117
114
  """Show memory statistics."""
118
- subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "stats"])
115
+ subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "stats"], check=False)
119
116
 
120
117
 
121
118
  @memory_app.command("patterns")
122
119
  def memory_patterns():
123
120
  """List stored patterns."""
124
- subprocess.run([sys.executable, "-m", "empathy_os.memory.control_panel", "patterns", "--list"])
121
+ subprocess.run(
122
+ [sys.executable, "-m", "empathy_os.memory.control_panel", "patterns", "--list"], check=False
123
+ )
125
124
 
126
125
 
127
126
  # =============================================================================
@@ -136,7 +135,10 @@ app.add_typer(provider_app, name="provider")
136
135
  def provider_show(
137
136
  ctx: typer.Context,
138
137
  set_provider: str | None = typer.Option(
139
- None, "--set", "-s", help="Set provider (anthropic, openai, google, ollama, hybrid)"
138
+ None,
139
+ "--set",
140
+ "-s",
141
+ help="Set provider (anthropic, openai, google, ollama, hybrid)",
140
142
  ),
141
143
  interactive: bool = typer.Option(False, "--interactive", "-i", help="Interactive setup wizard"),
142
144
  format_out: str = typer.Option("table", "--format", "-f", help="Output format (table, json)"),
@@ -153,7 +155,7 @@ def provider_show(
153
155
  if format_out != "table":
154
156
  args.extend(["-f", format_out])
155
157
 
156
- subprocess.run(args)
158
+ subprocess.run(args, check=False)
157
159
 
158
160
 
159
161
  @provider_app.command("registry")
@@ -164,7 +166,7 @@ def provider_registry(
164
166
  args = [sys.executable, "-m", "empathy_os.models.cli", "registry"]
165
167
  if provider_filter:
166
168
  args.extend(["--provider", provider_filter])
167
- subprocess.run(args)
169
+ subprocess.run(args, check=False)
168
170
 
169
171
 
170
172
  @provider_app.command("costs")
@@ -183,7 +185,8 @@ def provider_costs(
183
185
  str(input_tokens),
184
186
  "--output-tokens",
185
187
  str(output_tokens),
186
- ]
188
+ ],
189
+ check=False,
187
190
  )
188
191
 
189
192
 
@@ -201,7 +204,7 @@ def provider_telemetry(
201
204
  args.append("--costs")
202
205
  if providers:
203
206
  args.append("--providers")
204
- subprocess.run(args)
207
+ subprocess.run(args, check=False)
205
208
 
206
209
 
207
210
  # =============================================================================
@@ -211,9 +214,12 @@ def provider_telemetry(
211
214
 
212
215
  @app.command("scan")
213
216
  def scan(
214
- path: Path = typer.Argument(Path("."), help="Path to scan"),
217
+ path: Path = typer.Argument(Path(), help="Path to scan"),
215
218
  format_out: str = typer.Option(
216
- "text", "--format", "-f", help="Output format (text, json, sarif)"
219
+ "text",
220
+ "--format",
221
+ "-f",
222
+ help="Output format (text, json, sarif)",
217
223
  ),
218
224
  fix: bool = typer.Option(False, "--fix", help="Auto-fix safe issues"),
219
225
  staged: bool = typer.Option(False, "--staged", help="Only scan staged changes"),
@@ -227,7 +233,7 @@ def scan(
227
233
  if staged:
228
234
  args.append("--staged")
229
235
 
230
- result = subprocess.run(args, capture_output=False)
236
+ result = subprocess.run(args, check=False, capture_output=False)
231
237
  if result.returncode != 0:
232
238
  console.print("[yellow]Note: empathy-scan may not be installed[/yellow]")
233
239
  console.print("Install with: pip install empathy-framework[software]")
@@ -240,9 +246,12 @@ def scan(
240
246
 
241
247
  @app.command("inspect")
242
248
  def inspect_cmd(
243
- path: Path = typer.Argument(Path("."), help="Path to inspect"),
249
+ path: Path = typer.Argument(Path(), help="Path to inspect"),
244
250
  format_out: str = typer.Option(
245
- "text", "--format", "-f", help="Output format (text, json, sarif)"
251
+ "text",
252
+ "--format",
253
+ "-f",
254
+ help="Output format (text, json, sarif)",
246
255
  ),
247
256
  ):
248
257
  """Deep inspection with code analysis."""
@@ -250,7 +259,7 @@ def inspect_cmd(
250
259
  if format_out != "text":
251
260
  args.extend(["--format", format_out])
252
261
 
253
- result = subprocess.run(args, capture_output=False)
262
+ result = subprocess.run(args, check=False, capture_output=False)
254
263
  if result.returncode != 0:
255
264
  console.print("[yellow]Note: empathy-inspect may not be installed[/yellow]")
256
265
  console.print("Install with: pip install empathy-framework[software]")
@@ -264,11 +273,14 @@ def inspect_cmd(
264
273
  @app.command("sync-claude")
265
274
  def sync_claude(
266
275
  source: str = typer.Option(
267
- "patterns", "--source", "-s", help="Source to sync (patterns, bugs)"
276
+ "patterns",
277
+ "--source",
278
+ "-s",
279
+ help="Source to sync (patterns, bugs)",
268
280
  ),
269
281
  ):
270
282
  """Sync patterns to Claude Code memory."""
271
- subprocess.run(["empathy-sync-claude", "--source", source])
283
+ subprocess.run(["empathy-sync-claude", "--source", source], check=False)
272
284
 
273
285
 
274
286
  # =============================================================================
@@ -279,13 +291,24 @@ def sync_claude(
279
291
  @app.command("morning")
280
292
  def morning():
281
293
  """Start-of-day briefing with patterns, git context, and priorities."""
282
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "morning"])
294
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "morning"], check=False)
283
295
 
284
296
 
285
297
  @app.command("ship")
286
- def ship():
298
+ def ship(
299
+ tests_only: bool = typer.Option(False, "--tests-only", help="Run tests only"),
300
+ security_only: bool = typer.Option(False, "--security-only", help="Run security checks only"),
301
+ skip_sync: bool = typer.Option(False, "--skip-sync", help="Skip Claude sync"),
302
+ ):
287
303
  """Pre-commit validation (lint, format, tests, security)."""
288
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "ship"])
304
+ args = [sys.executable, "-m", "empathy_os.cli", "ship"]
305
+ if tests_only:
306
+ args.append("--tests-only")
307
+ if security_only:
308
+ args.append("--security-only")
309
+ if skip_sync:
310
+ args.append("--skip-sync")
311
+ subprocess.run(args, check=False)
289
312
 
290
313
 
291
314
  @app.command("health")
@@ -299,13 +322,13 @@ def health(
299
322
  args.append("--deep")
300
323
  if fix:
301
324
  args.append("--fix")
302
- subprocess.run(args)
325
+ subprocess.run(args, check=False)
303
326
 
304
327
 
305
328
  @app.command("fix-all")
306
329
  def fix_all():
307
330
  """Fix all lint and format issues."""
308
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "fix-all"])
331
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "fix-all"], check=False)
309
332
 
310
333
 
311
334
  @app.command("learn")
@@ -313,13 +336,15 @@ def learn(
313
336
  analyze: int = typer.Option(20, "--analyze", "-a", help="Number of commits to analyze"),
314
337
  ):
315
338
  """Learn patterns from commit history."""
316
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "learn", "--analyze", str(analyze)])
339
+ subprocess.run(
340
+ [sys.executable, "-m", "empathy_os.cli", "learn", "--analyze", str(analyze)], check=False
341
+ )
317
342
 
318
343
 
319
344
  @app.command("run")
320
345
  def run_repl():
321
346
  """Start interactive REPL mode."""
322
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "run"])
347
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "run"], check=False)
323
348
 
324
349
 
325
350
  # =============================================================================
@@ -333,18 +358,18 @@ app.add_typer(wizard_app, name="wizard")
333
358
  @wizard_app.command("list")
334
359
  def wizard_list():
335
360
  """List all available wizards."""
336
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "frameworks"])
361
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "frameworks"], check=False)
337
362
 
338
363
 
339
364
  @wizard_app.command("run")
340
365
  def wizard_run(
341
366
  name: str = typer.Argument(..., help="Wizard name to run"),
342
- path: Path = typer.Option(Path("."), "--path", "-p", help="Path to analyze"),
367
+ path: Path = typer.Option(Path(), "--path", "-p", help="Path to analyze"),
343
368
  ):
344
369
  """Run a specific wizard on your codebase."""
345
370
  console.print(f"[yellow]Running wizard:[/yellow] {name} on {path}")
346
371
  # Delegate to empathy-scan with wizard filter
347
- subprocess.run(["empathy-scan", str(path), "--wizards", name])
372
+ subprocess.run(["empathy-scan", str(path), "--wizards", name], check=False)
348
373
 
349
374
 
350
375
  # =============================================================================
@@ -358,16 +383,18 @@ app.add_typer(workflow_app, name="workflow")
358
383
  @workflow_app.command("list")
359
384
  def workflow_list():
360
385
  """List available multi-model workflows."""
361
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "workflow", "list"])
386
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "workflow", "list"], check=False)
362
387
 
363
388
 
364
389
  @workflow_app.command("run")
365
390
  def workflow_run(
366
391
  name: str = typer.Argument(..., help="Workflow name"),
367
- path: Path = typer.Option(Path("."), "--path", "-p", help="Path to run on"),
392
+ path: Path = typer.Option(Path(), "--path", "-p", help="Path to run on"),
368
393
  ):
369
394
  """Run a multi-model workflow."""
370
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "workflow", "run", name, str(path)])
395
+ subprocess.run(
396
+ [sys.executable, "-m", "empathy_os.cli", "workflow", "run", name, str(path)], check=False
397
+ )
371
398
 
372
399
 
373
400
  # =============================================================================
@@ -413,32 +440,32 @@ def cheatsheet():
413
440
  empathy wizard list Show available wizards
414
441
  empathy wizard run <name> Execute a wizard""",
415
442
  title="[bold blue]Empathy Framework Cheatsheet[/bold blue]",
416
- )
443
+ ),
417
444
  )
418
445
 
419
446
 
420
447
  @app.command("dashboard")
421
448
  def dashboard():
422
449
  """Launch visual dashboard."""
423
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "dashboard"])
450
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "dashboard"], check=False)
424
451
 
425
452
 
426
453
  @app.command("costs")
427
454
  def costs():
428
455
  """View API cost tracking."""
429
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "costs"])
456
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "costs"], check=False)
430
457
 
431
458
 
432
459
  @app.command("init")
433
460
  def init():
434
461
  """Create a new configuration file."""
435
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "init"])
462
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "init"], check=False)
436
463
 
437
464
 
438
465
  @app.command("status")
439
466
  def status():
440
467
  """What needs attention now."""
441
- subprocess.run([sys.executable, "-m", "empathy_os.cli", "status"])
468
+ subprocess.run([sys.executable, "-m", "empathy_os.cli", "status"], check=False)
442
469
 
443
470
 
444
471
  def main():
empathy_os/config.py CHANGED
@@ -1,5 +1,4 @@
1
- """
2
- Configuration Management for Empathy Framework
1
+ """Configuration Management for Empathy Framework
3
2
 
4
3
  Supports:
5
4
  - YAML configuration files
@@ -29,8 +28,7 @@ from empathy_os.workflows.config import ModelConfig
29
28
 
30
29
  @dataclass
31
30
  class EmpathyConfig:
32
- """
33
- Configuration for EmpathyOS instance
31
+ """Configuration for EmpathyOS instance
34
32
 
35
33
  Can be loaded from:
36
34
  - YAML file (.empathy.yml, empathy.config.yml)
@@ -93,8 +91,7 @@ class EmpathyConfig:
93
91
 
94
92
  @classmethod
95
93
  def from_yaml(cls, filepath: str) -> "EmpathyConfig":
96
- """
97
- Load configuration from YAML file
94
+ """Load configuration from YAML file
98
95
 
99
96
  Args:
100
97
  filepath: Path to YAML configuration file
@@ -115,10 +112,11 @@ class EmpathyConfig:
115
112
  This allows config files to contain settings for other
116
113
  components (e.g., model_preferences, workflows) without
117
114
  breaking EmpathyConfig loading.
115
+
118
116
  """
119
117
  if not YAML_AVAILABLE:
120
118
  raise ImportError(
121
- "PyYAML is required for YAML configuration. Install with: pip install pyyaml"
119
+ "PyYAML is required for YAML configuration. Install with: pip install pyyaml",
122
120
  )
123
121
 
124
122
  with open(filepath) as f:
@@ -140,15 +138,14 @@ class EmpathyConfig:
140
138
  filtered_data = {k: v for k, v in data.items() if k in known_fields}
141
139
 
142
140
  # Handle nested ModelConfig objects
143
- if "models" in filtered_data and filtered_data["models"]:
141
+ if filtered_data.get("models"):
144
142
  filtered_data["models"] = [ModelConfig(**m) for m in filtered_data["models"]]
145
143
 
146
144
  return cls(**filtered_data)
147
145
 
148
146
  @classmethod
149
147
  def from_json(cls, filepath: str) -> "EmpathyConfig":
150
- """
151
- Load configuration from JSON file
148
+ """Load configuration from JSON file
152
149
 
153
150
  Args:
154
151
  filepath: Path to JSON configuration file
@@ -162,6 +159,7 @@ class EmpathyConfig:
162
159
 
163
160
  Note:
164
161
  Unknown fields in the JSON file are silently ignored.
162
+
165
163
  """
166
164
  with open(filepath) as f:
167
165
  data = json.load(f)
@@ -176,8 +174,7 @@ class EmpathyConfig:
176
174
 
177
175
  @classmethod
178
176
  def from_env(cls, prefix: str = "EMPATHY_") -> "EmpathyConfig":
179
- """
180
- Load configuration from environment variables
177
+ """Load configuration from environment variables
181
178
 
182
179
  Environment variables should be prefixed with EMPATHY_
183
180
  and match config field names in uppercase.
@@ -197,6 +194,7 @@ class EmpathyConfig:
197
194
  >>> os.environ["EMPATHY_USER_ID"] = "alice"
198
195
  >>> config = EmpathyConfig.from_env()
199
196
  >>> print(config.user_id) # "alice"
197
+
200
198
  """
201
199
  from dataclasses import fields as dataclass_fields
202
200
 
@@ -244,8 +242,7 @@ class EmpathyConfig:
244
242
 
245
243
  @classmethod
246
244
  def from_file(cls, filepath: str | None = None) -> "EmpathyConfig":
247
- """
248
- Automatically detect and load configuration from file
245
+ """Automatically detect and load configuration from file
249
246
 
250
247
  Looks for configuration files in this order:
251
248
  1. Provided filepath
@@ -265,6 +262,7 @@ class EmpathyConfig:
265
262
  Example:
266
263
  >>> config = EmpathyConfig.from_file() # Auto-detect
267
264
  >>> config = EmpathyConfig.from_file("my-config.yml")
265
+
268
266
  """
269
267
  search_paths = [
270
268
  filepath,
@@ -280,15 +278,14 @@ class EmpathyConfig:
280
278
  if path and Path(path).exists():
281
279
  if path.endswith((".yml", ".yaml")):
282
280
  return cls.from_yaml(path)
283
- elif path.endswith(".json"):
281
+ if path.endswith(".json"):
284
282
  return cls.from_json(path)
285
283
 
286
284
  # No config file found - return default
287
285
  return cls()
288
286
 
289
287
  def to_yaml(self, filepath: str):
290
- """
291
- Save configuration to YAML file
288
+ """Save configuration to YAML file
292
289
 
293
290
  Args:
294
291
  filepath: Path to save YAML file
@@ -296,10 +293,11 @@ class EmpathyConfig:
296
293
  Example:
297
294
  >>> config = EmpathyConfig(user_id="alice", target_level=4)
298
295
  >>> config.to_yaml("my-config.yml")
296
+
299
297
  """
300
298
  if not YAML_AVAILABLE:
301
299
  raise ImportError(
302
- "PyYAML is required for YAML export. Install with: pip install pyyaml"
300
+ "PyYAML is required for YAML export. Install with: pip install pyyaml",
303
301
  )
304
302
 
305
303
  data = asdict(self)
@@ -308,8 +306,7 @@ class EmpathyConfig:
308
306
  yaml.dump(data, f, default_flow_style=False, sort_keys=False)
309
307
 
310
308
  def to_json(self, filepath: str, indent: int = 2):
311
- """
312
- Save configuration to JSON file
309
+ """Save configuration to JSON file
313
310
 
314
311
  Args:
315
312
  filepath: Path to save JSON file
@@ -318,6 +315,7 @@ class EmpathyConfig:
318
315
  Example:
319
316
  >>> config = EmpathyConfig(user_id="alice", target_level=4)
320
317
  >>> config.to_json("my-config.json")
318
+
321
319
  """
322
320
  data = asdict(self)
323
321
 
@@ -329,8 +327,7 @@ class EmpathyConfig:
329
327
  return asdict(self)
330
328
 
331
329
  def update(self, **kwargs):
332
- """
333
- Update configuration fields
330
+ """Update configuration fields
334
331
 
335
332
  Args:
336
333
  **kwargs: Fields to update
@@ -338,14 +335,14 @@ class EmpathyConfig:
338
335
  Example:
339
336
  >>> config = EmpathyConfig()
340
337
  >>> config.update(user_id="bob", target_level=5)
338
+
341
339
  """
342
340
  for key, value in kwargs.items():
343
341
  if hasattr(self, key):
344
342
  setattr(self, key, value)
345
343
 
346
344
  def merge(self, other: "EmpathyConfig") -> "EmpathyConfig":
347
- """
348
- Merge with another configuration (other takes precedence)
345
+ """Merge with another configuration (other takes precedence)
349
346
 
350
347
  Args:
351
348
  other: Configuration to merge
@@ -357,6 +354,7 @@ class EmpathyConfig:
357
354
  >>> base = EmpathyConfig(user_id="alice")
358
355
  >>> override = EmpathyConfig(target_level=5)
359
356
  >>> merged = base.merge(override)
357
+
360
358
  """
361
359
  # Start with base values
362
360
  base_dict = self.to_dict()
@@ -373,21 +371,21 @@ class EmpathyConfig:
373
371
  return EmpathyConfig(**base_dict)
374
372
 
375
373
  def validate(self) -> bool:
376
- """
377
- Validate configuration values
374
+ """Validate configuration values
378
375
 
379
376
  Returns:
380
377
  True if valid, raises ValueError if invalid
381
378
 
382
379
  Raises:
383
380
  ValueError: If configuration is invalid
381
+
384
382
  """
385
383
  if self.target_level not in range(1, 6):
386
384
  raise ValueError(f"target_level must be 1-5, got {self.target_level}")
387
385
 
388
386
  if not 0.0 <= self.confidence_threshold <= 1.0:
389
387
  raise ValueError(
390
- f"confidence_threshold must be 0.0-1.0, got {self.confidence_threshold}"
388
+ f"confidence_threshold must be 0.0-1.0, got {self.confidence_threshold}",
391
389
  )
392
390
 
393
391
  if not 0.0 <= self.pattern_confidence_threshold <= 1.0:
@@ -397,7 +395,7 @@ class EmpathyConfig:
397
395
  if self.persistence_backend not in ("sqlite", "json", "none"):
398
396
  backend_val = self.persistence_backend
399
397
  raise ValueError(
400
- f"persistence_backend must be 'sqlite', 'json', or 'none', got {backend_val}"
398
+ f"persistence_backend must be 'sqlite', 'json', or 'none', got {backend_val}",
401
399
  )
402
400
 
403
401
  return True
@@ -411,10 +409,11 @@ class EmpathyConfig:
411
409
 
412
410
 
413
411
  def load_config(
414
- filepath: str | None = None, use_env: bool = True, defaults: dict[str, Any] | None = None
412
+ filepath: str | None = None,
413
+ use_env: bool = True,
414
+ defaults: dict[str, Any] | None = None,
415
415
  ) -> EmpathyConfig:
416
- """
417
- Load configuration with flexible precedence
416
+ """Load configuration with flexible precedence
418
417
 
419
418
  Precedence (highest to lowest):
420
419
  1. Environment variables (if use_env=True)
@@ -436,6 +435,7 @@ def load_config(
436
435
 
437
436
  >>> # Load with custom defaults
438
437
  >>> config = load_config(defaults={"target_level": 4})
438
+
439
439
  """
440
440
  # Start with built-in defaults
441
441
  config = EmpathyConfig()