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.
- agents/compliance_anticipation_agent.py +113 -118
- agents/compliance_db.py +339 -0
- agents/epic_integration_wizard.py +37 -48
- agents/notifications.py +291 -0
- agents/trust_building_behaviors.py +66 -85
- coach_wizards/__init__.py +11 -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/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 +21 -14
- 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 +22 -25
- 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.5.6.dist-info → empathy_framework-3.7.0.dist-info}/METADATA +234 -30
- empathy_framework-3.7.0.dist-info/RECORD +105 -0
- empathy_healthcare_plugin/__init__.py +1 -2
- empathy_llm_toolkit/__init__.py +5 -6
- empathy_llm_toolkit/claude_memory.py +14 -15
- empathy_llm_toolkit/code_health.py +27 -19
- empathy_llm_toolkit/contextual_patterns.py +11 -12
- empathy_llm_toolkit/core.py +43 -49
- empathy_llm_toolkit/git_pattern_extractor.py +16 -12
- 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 +13 -11
- empathy_llm_toolkit/providers.py +27 -38
- empathy_llm_toolkit/session_status.py +18 -20
- empathy_llm_toolkit/state.py +20 -21
- empathy_os/__init__.py +72 -73
- empathy_os/cli.py +193 -98
- empathy_os/cli_unified.py +68 -41
- empathy_os/config.py +31 -31
- empathy_os/coordination.py +48 -54
- empathy_os/core.py +90 -99
- empathy_os/cost_tracker.py +20 -23
- empathy_os/discovery.py +9 -11
- empathy_os/emergence.py +20 -21
- empathy_os/exceptions.py +18 -30
- empathy_os/feedback_loops.py +27 -30
- empathy_os/levels.py +31 -34
- empathy_os/leverage_points.py +27 -28
- empathy_os/logging_config.py +11 -12
- empathy_os/monitoring.py +27 -27
- empathy_os/pattern_library.py +29 -28
- empathy_os/persistence.py +30 -34
- empathy_os/platform_utils.py +46 -47
- empathy_os/redis_config.py +14 -15
- empathy_os/redis_memory.py +53 -56
- empathy_os/templates.py +12 -11
- empathy_os/trust_building.py +44 -36
- empathy_os/workflow_commands.py +123 -31
- empathy_software_plugin/__init__.py +1 -2
- empathy_software_plugin/cli.py +32 -25
- empathy_software_plugin/plugin.py +4 -8
- empathy_framework-3.5.6.dist-info/RECORD +0 -103
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/WHEEL +0 -0
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/entry_points.txt +0 -0
- {empathy_framework-3.5.6.dist-info → empathy_framework-3.7.0.dist-info}/licenses/LICENSE +0 -0
- {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(
|
|
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,
|
|
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(
|
|
217
|
+
path: Path = typer.Argument(Path(), help="Path to scan"),
|
|
215
218
|
format_out: str = typer.Option(
|
|
216
|
-
"text",
|
|
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(
|
|
249
|
+
path: Path = typer.Argument(Path(), help="Path to inspect"),
|
|
244
250
|
format_out: str = typer.Option(
|
|
245
|
-
"text",
|
|
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",
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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,
|
|
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()
|