empathy-framework 4.6.6__py3-none-any.whl → 4.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.
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/METADATA +7 -6
- empathy_framework-4.7.0.dist-info/RECORD +354 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/top_level.txt +0 -2
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +9 -9
- empathy_llm_toolkit/agent_factory/__init__.py +6 -6
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +7 -10
- empathy_llm_toolkit/agents_md/__init__.py +22 -0
- empathy_llm_toolkit/agents_md/loader.py +218 -0
- empathy_llm_toolkit/agents_md/parser.py +271 -0
- empathy_llm_toolkit/agents_md/registry.py +307 -0
- empathy_llm_toolkit/commands/__init__.py +51 -0
- empathy_llm_toolkit/commands/context.py +375 -0
- empathy_llm_toolkit/commands/loader.py +301 -0
- empathy_llm_toolkit/commands/models.py +231 -0
- empathy_llm_toolkit/commands/parser.py +371 -0
- empathy_llm_toolkit/commands/registry.py +429 -0
- empathy_llm_toolkit/config/__init__.py +8 -8
- empathy_llm_toolkit/config/unified.py +3 -7
- empathy_llm_toolkit/context/__init__.py +22 -0
- empathy_llm_toolkit/context/compaction.py +455 -0
- empathy_llm_toolkit/context/manager.py +434 -0
- empathy_llm_toolkit/hooks/__init__.py +24 -0
- empathy_llm_toolkit/hooks/config.py +306 -0
- empathy_llm_toolkit/hooks/executor.py +289 -0
- empathy_llm_toolkit/hooks/registry.py +302 -0
- empathy_llm_toolkit/hooks/scripts/__init__.py +39 -0
- empathy_llm_toolkit/hooks/scripts/evaluate_session.py +201 -0
- empathy_llm_toolkit/hooks/scripts/first_time_init.py +285 -0
- empathy_llm_toolkit/hooks/scripts/pre_compact.py +207 -0
- empathy_llm_toolkit/hooks/scripts/session_end.py +183 -0
- empathy_llm_toolkit/hooks/scripts/session_start.py +163 -0
- empathy_llm_toolkit/hooks/scripts/suggest_compact.py +225 -0
- empathy_llm_toolkit/learning/__init__.py +30 -0
- empathy_llm_toolkit/learning/evaluator.py +438 -0
- empathy_llm_toolkit/learning/extractor.py +514 -0
- empathy_llm_toolkit/learning/storage.py +560 -0
- empathy_llm_toolkit/providers.py +4 -11
- empathy_llm_toolkit/security/__init__.py +17 -17
- empathy_llm_toolkit/utils/tokens.py +2 -5
- empathy_os/__init__.py +202 -70
- empathy_os/cache_monitor.py +5 -3
- empathy_os/cli/__init__.py +11 -55
- empathy_os/cli/__main__.py +29 -15
- empathy_os/cli/commands/inspection.py +21 -12
- empathy_os/cli/commands/memory.py +4 -12
- empathy_os/cli/commands/profiling.py +198 -0
- empathy_os/cli/commands/utilities.py +27 -7
- empathy_os/cli.py +28 -57
- empathy_os/cli_unified.py +525 -1164
- empathy_os/cost_tracker.py +9 -3
- empathy_os/dashboard/server.py +200 -2
- empathy_os/hot_reload/__init__.py +7 -7
- empathy_os/hot_reload/config.py +6 -7
- empathy_os/hot_reload/integration.py +35 -35
- empathy_os/hot_reload/reloader.py +57 -57
- empathy_os/hot_reload/watcher.py +28 -28
- empathy_os/hot_reload/websocket.py +2 -2
- empathy_os/memory/__init__.py +11 -4
- empathy_os/memory/claude_memory.py +1 -1
- empathy_os/memory/cross_session.py +8 -12
- empathy_os/memory/edges.py +6 -6
- empathy_os/memory/file_session.py +770 -0
- empathy_os/memory/graph.py +30 -30
- empathy_os/memory/nodes.py +6 -6
- empathy_os/memory/short_term.py +15 -9
- empathy_os/memory/unified.py +606 -140
- empathy_os/meta_workflows/agent_creator.py +3 -9
- empathy_os/meta_workflows/cli_meta_workflows.py +113 -53
- empathy_os/meta_workflows/form_engine.py +6 -18
- empathy_os/meta_workflows/intent_detector.py +64 -24
- empathy_os/meta_workflows/models.py +3 -1
- empathy_os/meta_workflows/pattern_learner.py +13 -31
- empathy_os/meta_workflows/plan_generator.py +55 -47
- empathy_os/meta_workflows/session_context.py +2 -3
- empathy_os/meta_workflows/workflow.py +20 -51
- empathy_os/models/cli.py +2 -2
- empathy_os/models/tasks.py +1 -2
- empathy_os/models/telemetry.py +4 -1
- empathy_os/models/token_estimator.py +3 -1
- empathy_os/monitoring/alerts.py +938 -9
- empathy_os/monitoring/alerts_cli.py +346 -183
- empathy_os/orchestration/execution_strategies.py +12 -29
- empathy_os/orchestration/pattern_learner.py +20 -26
- empathy_os/orchestration/real_tools.py +6 -15
- empathy_os/platform_utils.py +2 -1
- empathy_os/plugins/__init__.py +2 -2
- empathy_os/plugins/base.py +64 -64
- empathy_os/plugins/registry.py +32 -32
- empathy_os/project_index/index.py +49 -15
- empathy_os/project_index/models.py +1 -2
- empathy_os/project_index/reports.py +1 -1
- empathy_os/project_index/scanner.py +1 -0
- empathy_os/redis_memory.py +10 -7
- empathy_os/resilience/__init__.py +1 -1
- empathy_os/resilience/health.py +10 -10
- empathy_os/routing/__init__.py +7 -7
- empathy_os/routing/chain_executor.py +37 -37
- empathy_os/routing/classifier.py +36 -36
- empathy_os/routing/smart_router.py +40 -40
- empathy_os/routing/{wizard_registry.py → workflow_registry.py} +47 -47
- empathy_os/scaffolding/__init__.py +8 -8
- empathy_os/scaffolding/__main__.py +1 -1
- empathy_os/scaffolding/cli.py +28 -28
- empathy_os/socratic/__init__.py +3 -19
- empathy_os/socratic/ab_testing.py +25 -36
- empathy_os/socratic/blueprint.py +38 -38
- empathy_os/socratic/cli.py +34 -20
- empathy_os/socratic/collaboration.py +30 -28
- empathy_os/socratic/domain_templates.py +9 -1
- empathy_os/socratic/embeddings.py +17 -13
- empathy_os/socratic/engine.py +135 -70
- empathy_os/socratic/explainer.py +70 -60
- empathy_os/socratic/feedback.py +24 -19
- empathy_os/socratic/forms.py +15 -10
- empathy_os/socratic/generator.py +51 -35
- empathy_os/socratic/llm_analyzer.py +25 -23
- empathy_os/socratic/mcp_server.py +99 -159
- empathy_os/socratic/session.py +19 -13
- empathy_os/socratic/storage.py +98 -67
- empathy_os/socratic/success.py +38 -27
- empathy_os/socratic/visual_editor.py +51 -39
- empathy_os/socratic/web_ui.py +99 -66
- empathy_os/telemetry/cli.py +3 -1
- empathy_os/telemetry/usage_tracker.py +1 -3
- empathy_os/test_generator/__init__.py +3 -3
- empathy_os/test_generator/cli.py +28 -28
- empathy_os/test_generator/generator.py +64 -66
- empathy_os/test_generator/risk_analyzer.py +11 -11
- empathy_os/vscode_bridge.py +173 -0
- empathy_os/workflows/__init__.py +212 -120
- empathy_os/workflows/batch_processing.py +8 -24
- empathy_os/workflows/bug_predict.py +1 -1
- empathy_os/workflows/code_review.py +20 -5
- empathy_os/workflows/code_review_pipeline.py +13 -8
- empathy_os/workflows/keyboard_shortcuts/workflow.py +6 -2
- empathy_os/workflows/manage_documentation.py +1 -0
- empathy_os/workflows/orchestrated_health_check.py +6 -11
- empathy_os/workflows/orchestrated_release_prep.py +3 -3
- empathy_os/workflows/pr_review.py +18 -10
- empathy_os/workflows/progressive/__init__.py +2 -12
- empathy_os/workflows/progressive/cli.py +14 -37
- empathy_os/workflows/progressive/core.py +12 -12
- empathy_os/workflows/progressive/orchestrator.py +166 -144
- empathy_os/workflows/progressive/reports.py +22 -31
- empathy_os/workflows/progressive/telemetry.py +8 -14
- empathy_os/workflows/progressive/test_gen.py +29 -48
- empathy_os/workflows/progressive/workflow.py +31 -70
- empathy_os/workflows/release_prep.py +21 -6
- empathy_os/workflows/release_prep_crew.py +1 -0
- empathy_os/workflows/secure_release.py +13 -6
- empathy_os/workflows/security_audit.py +8 -3
- empathy_os/workflows/test_coverage_boost_crew.py +3 -2
- empathy_os/workflows/test_maintenance_crew.py +1 -0
- empathy_os/workflows/test_runner.py +16 -12
- empathy_software_plugin/SOFTWARE_PLUGIN_README.md +25 -703
- empathy_software_plugin/cli.py +0 -122
- coach_wizards/__init__.py +0 -45
- coach_wizards/accessibility_wizard.py +0 -91
- coach_wizards/api_wizard.py +0 -91
- coach_wizards/base_wizard.py +0 -209
- coach_wizards/cicd_wizard.py +0 -91
- coach_wizards/code_reviewer_README.md +0 -60
- coach_wizards/code_reviewer_wizard.py +0 -180
- coach_wizards/compliance_wizard.py +0 -91
- coach_wizards/database_wizard.py +0 -91
- coach_wizards/debugging_wizard.py +0 -91
- coach_wizards/documentation_wizard.py +0 -91
- coach_wizards/generate_wizards.py +0 -347
- coach_wizards/localization_wizard.py +0 -173
- coach_wizards/migration_wizard.py +0 -91
- coach_wizards/monitoring_wizard.py +0 -91
- coach_wizards/observability_wizard.py +0 -91
- coach_wizards/performance_wizard.py +0 -91
- coach_wizards/prompt_engineering_wizard.py +0 -661
- coach_wizards/refactoring_wizard.py +0 -91
- coach_wizards/scaling_wizard.py +0 -90
- coach_wizards/security_wizard.py +0 -92
- coach_wizards/testing_wizard.py +0 -91
- empathy_framework-4.6.6.dist-info/RECORD +0 -410
- empathy_llm_toolkit/wizards/__init__.py +0 -43
- empathy_llm_toolkit/wizards/base_wizard.py +0 -364
- empathy_llm_toolkit/wizards/customer_support_wizard.py +0 -190
- empathy_llm_toolkit/wizards/healthcare_wizard.py +0 -378
- empathy_llm_toolkit/wizards/patient_assessment_README.md +0 -64
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +0 -193
- empathy_llm_toolkit/wizards/technology_wizard.py +0 -209
- empathy_os/wizard_factory_cli.py +0 -170
- empathy_software_plugin/wizards/__init__.py +0 -42
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +0 -395
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +0 -511
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +0 -503
- empathy_software_plugin/wizards/ai_context_wizard.py +0 -441
- empathy_software_plugin/wizards/ai_documentation_wizard.py +0 -503
- empathy_software_plugin/wizards/base_wizard.py +0 -288
- empathy_software_plugin/wizards/book_chapter_wizard.py +0 -519
- empathy_software_plugin/wizards/code_review_wizard.py +0 -604
- empathy_software_plugin/wizards/debugging/__init__.py +0 -50
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +0 -414
- empathy_software_plugin/wizards/debugging/config_loaders.py +0 -446
- empathy_software_plugin/wizards/debugging/fix_applier.py +0 -469
- empathy_software_plugin/wizards/debugging/language_patterns.py +0 -385
- empathy_software_plugin/wizards/debugging/linter_parsers.py +0 -470
- empathy_software_plugin/wizards/debugging/verification.py +0 -369
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +0 -537
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +0 -816
- empathy_software_plugin/wizards/multi_model_wizard.py +0 -501
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +0 -422
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +0 -400
- empathy_software_plugin/wizards/performance/__init__.py +0 -9
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +0 -221
- empathy_software_plugin/wizards/performance/profiler_parsers.py +0 -278
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +0 -429
- empathy_software_plugin/wizards/performance_profiling_wizard.py +0 -305
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +0 -425
- empathy_software_plugin/wizards/rag_pattern_wizard.py +0 -461
- empathy_software_plugin/wizards/security/__init__.py +0 -32
- empathy_software_plugin/wizards/security/exploit_analyzer.py +0 -290
- empathy_software_plugin/wizards/security/owasp_patterns.py +0 -241
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +0 -604
- empathy_software_plugin/wizards/security_analysis_wizard.py +0 -322
- empathy_software_plugin/wizards/security_learning_wizard.py +0 -740
- empathy_software_plugin/wizards/tech_debt_wizard.py +0 -726
- empathy_software_plugin/wizards/testing/__init__.py +0 -27
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +0 -459
- empathy_software_plugin/wizards/testing/quality_analyzer.py +0 -525
- empathy_software_plugin/wizards/testing/test_suggester.py +0 -533
- empathy_software_plugin/wizards/testing_wizard.py +0 -274
- wizards/__init__.py +0 -82
- wizards/admission_assessment_wizard.py +0 -644
- wizards/care_plan.py +0 -321
- wizards/clinical_assessment.py +0 -769
- wizards/discharge_planning.py +0 -77
- wizards/discharge_summary_wizard.py +0 -468
- wizards/dosage_calculation.py +0 -497
- wizards/incident_report_wizard.py +0 -454
- wizards/medication_reconciliation.py +0 -85
- wizards/nursing_assessment.py +0 -171
- wizards/patient_education.py +0 -654
- wizards/quality_improvement.py +0 -705
- wizards/sbar_report.py +0 -324
- wizards/sbar_wizard.py +0 -608
- wizards/shift_handoff_wizard.py +0 -535
- wizards/soap_note_wizard.py +0 -679
- wizards/treatment_plan.py +0 -15
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/WHEEL +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/entry_points.txt +0 -0
- {empathy_framework-4.6.6.dist-info → empathy_framework-4.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Agent Loader
|
|
2
|
+
|
|
3
|
+
Loads agents from directory structures.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from empathy_llm_toolkit.agents_md.parser import MarkdownAgentParser
|
|
14
|
+
from empathy_llm_toolkit.config.unified import UnifiedAgentConfig
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentLoader:
|
|
20
|
+
"""Loader for discovering and loading markdown agent files.
|
|
21
|
+
|
|
22
|
+
Scans directories for .md files with agent definitions and loads them
|
|
23
|
+
into UnifiedAgentConfig instances.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
loader = AgentLoader()
|
|
27
|
+
|
|
28
|
+
# Load a single agent
|
|
29
|
+
config = loader.load("agents/architect.md")
|
|
30
|
+
|
|
31
|
+
# Load all agents from a directory
|
|
32
|
+
agents = loader.load_directory("agents/")
|
|
33
|
+
|
|
34
|
+
# Discover and iterate agents lazily
|
|
35
|
+
for config in loader.discover("agents/"):
|
|
36
|
+
print(config.name)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, parser: MarkdownAgentParser | None = None):
|
|
40
|
+
"""Initialize the loader.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
parser: Optional custom parser instance
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
self.parser = parser or MarkdownAgentParser()
|
|
47
|
+
|
|
48
|
+
def load(self, file_path: str | Path) -> UnifiedAgentConfig:
|
|
49
|
+
"""Load a single agent file.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
file_path: Path to the agent markdown file
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
UnifiedAgentConfig instance
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
return self.parser.parse_file(file_path)
|
|
59
|
+
|
|
60
|
+
def load_directory(
|
|
61
|
+
self,
|
|
62
|
+
directory: str | Path,
|
|
63
|
+
recursive: bool = False,
|
|
64
|
+
) -> dict[str, UnifiedAgentConfig]:
|
|
65
|
+
"""Load all agents from a directory.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
directory: Directory to scan for .md files
|
|
69
|
+
recursive: If True, scan subdirectories
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Dictionary mapping agent names to configs
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
agents = {}
|
|
76
|
+
|
|
77
|
+
for config in self.discover(directory, recursive=recursive):
|
|
78
|
+
if config.name in agents:
|
|
79
|
+
logger.warning(
|
|
80
|
+
"Duplicate agent name '%s' - keeping first occurrence",
|
|
81
|
+
config.name,
|
|
82
|
+
)
|
|
83
|
+
continue
|
|
84
|
+
agents[config.name] = config
|
|
85
|
+
|
|
86
|
+
logger.info("Loaded %d agent(s) from %s", len(agents), directory)
|
|
87
|
+
return agents
|
|
88
|
+
|
|
89
|
+
def discover(
|
|
90
|
+
self,
|
|
91
|
+
directory: str | Path,
|
|
92
|
+
recursive: bool = False,
|
|
93
|
+
) -> Iterator[UnifiedAgentConfig]:
|
|
94
|
+
"""Discover and yield agents from a directory.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
directory: Directory to scan
|
|
98
|
+
recursive: If True, scan subdirectories
|
|
99
|
+
|
|
100
|
+
Yields:
|
|
101
|
+
UnifiedAgentConfig instances
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
directory = Path(directory)
|
|
105
|
+
|
|
106
|
+
if not directory.exists():
|
|
107
|
+
logger.warning("Agent directory not found: %s", directory)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
if not directory.is_dir():
|
|
111
|
+
raise ValueError(f"Not a directory: {directory}")
|
|
112
|
+
|
|
113
|
+
# Get pattern for globbing
|
|
114
|
+
pattern = "**/*.md" if recursive else "*.md"
|
|
115
|
+
|
|
116
|
+
for file_path in sorted(directory.glob(pattern)):
|
|
117
|
+
if not file_path.is_file():
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
# Skip files that don't look like agent definitions
|
|
121
|
+
if file_path.name.startswith("_"):
|
|
122
|
+
continue
|
|
123
|
+
if file_path.name.upper() in ("README.MD", "CHANGELOG.MD"):
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
config = self.parser.parse_file(file_path)
|
|
128
|
+
yield config
|
|
129
|
+
except ValueError as e:
|
|
130
|
+
logger.warning("Skipping invalid agent file %s: %s", file_path, e)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.error("Error loading agent file %s: %s", file_path, e)
|
|
133
|
+
|
|
134
|
+
def validate_directory(
|
|
135
|
+
self,
|
|
136
|
+
directory: str | Path,
|
|
137
|
+
recursive: bool = False,
|
|
138
|
+
) -> dict[str, list[str]]:
|
|
139
|
+
"""Validate all agent files in a directory.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
directory: Directory to validate
|
|
143
|
+
recursive: If True, scan subdirectories
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Dictionary mapping file paths to lists of errors
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
directory = Path(directory)
|
|
150
|
+
results = {}
|
|
151
|
+
|
|
152
|
+
pattern = "**/*.md" if recursive else "*.md"
|
|
153
|
+
|
|
154
|
+
for file_path in sorted(directory.glob(pattern)):
|
|
155
|
+
if not file_path.is_file():
|
|
156
|
+
continue
|
|
157
|
+
if file_path.name.startswith("_"):
|
|
158
|
+
continue
|
|
159
|
+
if file_path.name.upper() in ("README.MD", "CHANGELOG.MD"):
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
errors = self.parser.validate_file(file_path)
|
|
163
|
+
if errors:
|
|
164
|
+
results[str(file_path)] = errors
|
|
165
|
+
|
|
166
|
+
return results
|
|
167
|
+
|
|
168
|
+
def get_agent_names(
|
|
169
|
+
self,
|
|
170
|
+
directory: str | Path,
|
|
171
|
+
recursive: bool = False,
|
|
172
|
+
) -> list[str]:
|
|
173
|
+
"""Get list of agent names in a directory without fully loading.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
directory: Directory to scan
|
|
177
|
+
recursive: If True, scan subdirectories
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
List of agent names
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
names = []
|
|
184
|
+
for config in self.discover(directory, recursive=recursive):
|
|
185
|
+
names.append(config.name)
|
|
186
|
+
return names
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def load_agents_from_paths(
|
|
190
|
+
paths: list[str | Path],
|
|
191
|
+
parser: MarkdownAgentParser | None = None,
|
|
192
|
+
) -> dict[str, UnifiedAgentConfig]:
|
|
193
|
+
"""Load agents from multiple paths (files or directories).
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
paths: List of file or directory paths
|
|
197
|
+
parser: Optional custom parser
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Dictionary mapping agent names to configs
|
|
201
|
+
|
|
202
|
+
"""
|
|
203
|
+
loader = AgentLoader(parser=parser)
|
|
204
|
+
agents = {}
|
|
205
|
+
|
|
206
|
+
for path in paths:
|
|
207
|
+
path = Path(path)
|
|
208
|
+
|
|
209
|
+
if path.is_file():
|
|
210
|
+
config = loader.load(path)
|
|
211
|
+
agents[config.name] = config
|
|
212
|
+
elif path.is_dir():
|
|
213
|
+
dir_agents = loader.load_directory(path)
|
|
214
|
+
agents.update(dir_agents)
|
|
215
|
+
else:
|
|
216
|
+
logger.warning("Path not found: %s", path)
|
|
217
|
+
|
|
218
|
+
return agents
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Markdown Agent Parser
|
|
2
|
+
|
|
3
|
+
Parses Markdown files with YAML frontmatter into UnifiedAgentConfig.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from empathy_llm_toolkit.config.unified import ModelTier, Provider, UnifiedAgentConfig
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# YAML frontmatter regex pattern
|
|
19
|
+
FRONTMATTER_PATTERN = re.compile(
|
|
20
|
+
r"^---\s*\n(.*?)\n---\s*\n",
|
|
21
|
+
re.DOTALL,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MarkdownAgentParser:
|
|
26
|
+
"""Parser for Markdown agent definition files.
|
|
27
|
+
|
|
28
|
+
Parses files with YAML frontmatter containing agent configuration,
|
|
29
|
+
followed by Markdown content that becomes the system prompt.
|
|
30
|
+
|
|
31
|
+
Example file format:
|
|
32
|
+
---
|
|
33
|
+
name: architect
|
|
34
|
+
description: Software architecture specialist
|
|
35
|
+
model: capable
|
|
36
|
+
tools: Read, Grep, Glob
|
|
37
|
+
empathy_level: 4
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
You are an expert software architect...
|
|
41
|
+
|
|
42
|
+
Example usage:
|
|
43
|
+
parser = MarkdownAgentParser()
|
|
44
|
+
config = parser.parse_file("agents/architect.md")
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Mapping from string model names to ModelTier
|
|
48
|
+
MODEL_TIER_MAP = {
|
|
49
|
+
"cheap": ModelTier.CHEAP,
|
|
50
|
+
"haiku": ModelTier.CHEAP,
|
|
51
|
+
"capable": ModelTier.CAPABLE,
|
|
52
|
+
"sonnet": ModelTier.CAPABLE,
|
|
53
|
+
"premium": ModelTier.PREMIUM,
|
|
54
|
+
"opus": ModelTier.PREMIUM,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Mapping from string provider names to Provider
|
|
58
|
+
PROVIDER_MAP = {
|
|
59
|
+
"anthropic": Provider.ANTHROPIC,
|
|
60
|
+
"openai": Provider.OPENAI,
|
|
61
|
+
"local": Provider.LOCAL,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def __init__(self):
|
|
65
|
+
"""Initialize the parser."""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def parse_file(self, file_path: str | Path) -> UnifiedAgentConfig:
|
|
69
|
+
"""Parse a Markdown agent file into UnifiedAgentConfig.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
file_path: Path to the Markdown agent file
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
UnifiedAgentConfig instance
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
FileNotFoundError: If file doesn't exist
|
|
79
|
+
ValueError: If file format is invalid
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
file_path = Path(file_path)
|
|
83
|
+
|
|
84
|
+
if not file_path.exists():
|
|
85
|
+
raise FileNotFoundError(f"Agent file not found: {file_path}")
|
|
86
|
+
|
|
87
|
+
with open(file_path, encoding="utf-8") as f:
|
|
88
|
+
content = f.read()
|
|
89
|
+
|
|
90
|
+
return self.parse_content(content, source=str(file_path))
|
|
91
|
+
|
|
92
|
+
def parse_content(
|
|
93
|
+
self,
|
|
94
|
+
content: str,
|
|
95
|
+
source: str = "unknown",
|
|
96
|
+
) -> UnifiedAgentConfig:
|
|
97
|
+
"""Parse Markdown content into UnifiedAgentConfig.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
content: Markdown content with YAML frontmatter
|
|
101
|
+
source: Source identifier for error messages
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
UnifiedAgentConfig instance
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ValueError: If content format is invalid
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
# Extract frontmatter
|
|
111
|
+
match = FRONTMATTER_PATTERN.match(content)
|
|
112
|
+
|
|
113
|
+
if not match:
|
|
114
|
+
raise ValueError(f"Invalid agent file format - missing YAML frontmatter: {source}")
|
|
115
|
+
|
|
116
|
+
frontmatter_yaml = match.group(1)
|
|
117
|
+
body = content[match.end() :].strip()
|
|
118
|
+
|
|
119
|
+
# Parse YAML
|
|
120
|
+
try:
|
|
121
|
+
import yaml
|
|
122
|
+
|
|
123
|
+
frontmatter = yaml.safe_load(frontmatter_yaml) or {}
|
|
124
|
+
except yaml.YAMLError as e:
|
|
125
|
+
raise ValueError(f"Invalid YAML frontmatter in {source}: {e}")
|
|
126
|
+
|
|
127
|
+
return self._create_config(frontmatter, body, source)
|
|
128
|
+
|
|
129
|
+
def _create_config(
|
|
130
|
+
self,
|
|
131
|
+
frontmatter: dict[str, Any],
|
|
132
|
+
body: str,
|
|
133
|
+
source: str,
|
|
134
|
+
) -> UnifiedAgentConfig:
|
|
135
|
+
"""Create UnifiedAgentConfig from parsed data.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
frontmatter: Parsed YAML frontmatter
|
|
139
|
+
body: Markdown body content
|
|
140
|
+
source: Source identifier
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
UnifiedAgentConfig instance
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
# Required field
|
|
147
|
+
name = frontmatter.get("name")
|
|
148
|
+
if not name:
|
|
149
|
+
raise ValueError(f"Agent file missing required 'name' field: {source}")
|
|
150
|
+
|
|
151
|
+
# Parse model tier
|
|
152
|
+
model_str = frontmatter.get("model", "capable").lower()
|
|
153
|
+
model_tier = self.MODEL_TIER_MAP.get(model_str, ModelTier.CAPABLE)
|
|
154
|
+
|
|
155
|
+
# Parse provider
|
|
156
|
+
provider_str = frontmatter.get("provider", "anthropic").lower()
|
|
157
|
+
provider = self.PROVIDER_MAP.get(provider_str, Provider.ANTHROPIC)
|
|
158
|
+
|
|
159
|
+
# Parse tools list
|
|
160
|
+
tools_raw = frontmatter.get("tools", [])
|
|
161
|
+
if isinstance(tools_raw, str):
|
|
162
|
+
# Handle comma-separated string
|
|
163
|
+
tools = [t.strip() for t in tools_raw.split(",")]
|
|
164
|
+
else:
|
|
165
|
+
tools = list(tools_raw)
|
|
166
|
+
|
|
167
|
+
# Parse capabilities
|
|
168
|
+
capabilities = frontmatter.get("capabilities", [])
|
|
169
|
+
if isinstance(capabilities, str):
|
|
170
|
+
capabilities = [c.strip() for c in capabilities.split(",")]
|
|
171
|
+
|
|
172
|
+
# Build config
|
|
173
|
+
config = UnifiedAgentConfig(
|
|
174
|
+
name=name,
|
|
175
|
+
role=frontmatter.get("role", name),
|
|
176
|
+
description=frontmatter.get("description", ""),
|
|
177
|
+
model_tier=model_tier,
|
|
178
|
+
model_override=frontmatter.get("model_override"),
|
|
179
|
+
provider=provider,
|
|
180
|
+
empathy_level=int(frontmatter.get("empathy_level", 4)),
|
|
181
|
+
memory_enabled=frontmatter.get("memory_enabled", True),
|
|
182
|
+
pattern_learning=frontmatter.get("pattern_learning", True),
|
|
183
|
+
cost_tracking=frontmatter.get("cost_tracking", True),
|
|
184
|
+
use_patterns=frontmatter.get("use_patterns", True),
|
|
185
|
+
temperature=float(frontmatter.get("temperature", 0.7)),
|
|
186
|
+
max_tokens=int(frontmatter.get("max_tokens", 4096)),
|
|
187
|
+
timeout=int(frontmatter.get("timeout", 120)),
|
|
188
|
+
retry_attempts=int(frontmatter.get("retry_attempts", 3)),
|
|
189
|
+
retry_delay=float(frontmatter.get("retry_delay", 1.0)),
|
|
190
|
+
system_prompt=body if body else None,
|
|
191
|
+
tools=tools,
|
|
192
|
+
capabilities=capabilities,
|
|
193
|
+
framework_options=frontmatter.get("framework_options", {}),
|
|
194
|
+
extra={
|
|
195
|
+
"source_file": source,
|
|
196
|
+
"raw_frontmatter": frontmatter,
|
|
197
|
+
"interaction_mode": frontmatter.get("interaction_mode", "standard"),
|
|
198
|
+
"socratic_config": frontmatter.get("socratic_config", {}),
|
|
199
|
+
},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
logger.debug("Parsed agent config: %s from %s", name, source)
|
|
203
|
+
return config
|
|
204
|
+
|
|
205
|
+
def validate_file(self, file_path: str | Path) -> list[str]:
|
|
206
|
+
"""Validate a Markdown agent file without fully parsing.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
file_path: Path to validate
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of validation error messages (empty if valid)
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
errors = []
|
|
216
|
+
file_path = Path(file_path)
|
|
217
|
+
|
|
218
|
+
if not file_path.exists():
|
|
219
|
+
return [f"File not found: {file_path}"]
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
with open(file_path, encoding="utf-8") as f:
|
|
223
|
+
content = f.read()
|
|
224
|
+
except OSError as e:
|
|
225
|
+
return [f"Cannot read file: {e}"]
|
|
226
|
+
|
|
227
|
+
# Check frontmatter
|
|
228
|
+
match = FRONTMATTER_PATTERN.match(content)
|
|
229
|
+
if not match:
|
|
230
|
+
errors.append("Missing YAML frontmatter (must start with ---)")
|
|
231
|
+
return errors
|
|
232
|
+
|
|
233
|
+
# Parse YAML
|
|
234
|
+
try:
|
|
235
|
+
import yaml
|
|
236
|
+
|
|
237
|
+
frontmatter = yaml.safe_load(match.group(1)) or {}
|
|
238
|
+
except yaml.YAMLError as e:
|
|
239
|
+
errors.append(f"Invalid YAML: {e}")
|
|
240
|
+
return errors
|
|
241
|
+
|
|
242
|
+
# Check required fields
|
|
243
|
+
if not frontmatter.get("name"):
|
|
244
|
+
errors.append("Missing required field: name")
|
|
245
|
+
|
|
246
|
+
# Validate model tier
|
|
247
|
+
model = frontmatter.get("model", "").lower()
|
|
248
|
+
if model and model not in self.MODEL_TIER_MAP:
|
|
249
|
+
errors.append(
|
|
250
|
+
f"Invalid model '{model}'. Valid options: {', '.join(self.MODEL_TIER_MAP.keys())}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Validate provider
|
|
254
|
+
provider = frontmatter.get("provider", "").lower()
|
|
255
|
+
if provider and provider not in self.PROVIDER_MAP:
|
|
256
|
+
errors.append(
|
|
257
|
+
f"Invalid provider '{provider}'. "
|
|
258
|
+
f"Valid options: {', '.join(self.PROVIDER_MAP.keys())}"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Validate empathy level
|
|
262
|
+
empathy_level = frontmatter.get("empathy_level")
|
|
263
|
+
if empathy_level is not None:
|
|
264
|
+
try:
|
|
265
|
+
level = int(empathy_level)
|
|
266
|
+
if not 1 <= level <= 5:
|
|
267
|
+
errors.append(f"empathy_level must be 1-5, got {level}")
|
|
268
|
+
except (TypeError, ValueError):
|
|
269
|
+
errors.append(f"empathy_level must be an integer, got {empathy_level}")
|
|
270
|
+
|
|
271
|
+
return errors
|