empathy-framework 4.6.2__py3-none-any.whl → 4.6.3__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 (53) hide show
  1. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/METADATA +1 -1
  2. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/RECORD +53 -20
  3. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/WHEEL +1 -1
  4. empathy_os/__init__.py +1 -1
  5. empathy_os/cli.py +361 -32
  6. empathy_os/config/xml_config.py +8 -3
  7. empathy_os/core.py +37 -4
  8. empathy_os/leverage_points.py +2 -1
  9. empathy_os/memory/short_term.py +45 -1
  10. empathy_os/meta_workflows/agent_creator 2.py +254 -0
  11. empathy_os/meta_workflows/builtin_templates 2.py +567 -0
  12. empathy_os/meta_workflows/cli_meta_workflows 2.py +1551 -0
  13. empathy_os/meta_workflows/form_engine 2.py +304 -0
  14. empathy_os/meta_workflows/intent_detector 2.py +298 -0
  15. empathy_os/meta_workflows/pattern_learner 2.py +754 -0
  16. empathy_os/meta_workflows/session_context 2.py +398 -0
  17. empathy_os/meta_workflows/template_registry 2.py +229 -0
  18. empathy_os/meta_workflows/workflow 2.py +980 -0
  19. empathy_os/models/token_estimator.py +16 -9
  20. empathy_os/models/validation.py +7 -1
  21. empathy_os/orchestration/pattern_learner 2.py +699 -0
  22. empathy_os/orchestration/real_tools 2.py +938 -0
  23. empathy_os/orchestration/real_tools.py +4 -2
  24. empathy_os/socratic/__init__ 2.py +273 -0
  25. empathy_os/socratic/ab_testing 2.py +969 -0
  26. empathy_os/socratic/blueprint 2.py +532 -0
  27. empathy_os/socratic/cli 2.py +689 -0
  28. empathy_os/socratic/collaboration 2.py +1112 -0
  29. empathy_os/socratic/domain_templates 2.py +916 -0
  30. empathy_os/socratic/embeddings 2.py +734 -0
  31. empathy_os/socratic/engine 2.py +729 -0
  32. empathy_os/socratic/explainer 2.py +663 -0
  33. empathy_os/socratic/feedback 2.py +767 -0
  34. empathy_os/socratic/forms 2.py +624 -0
  35. empathy_os/socratic/generator 2.py +716 -0
  36. empathy_os/socratic/llm_analyzer 2.py +635 -0
  37. empathy_os/socratic/mcp_server 2.py +751 -0
  38. empathy_os/socratic/session 2.py +306 -0
  39. empathy_os/socratic/storage 2.py +635 -0
  40. empathy_os/socratic/storage.py +2 -1
  41. empathy_os/socratic/success 2.py +719 -0
  42. empathy_os/socratic/visual_editor 2.py +812 -0
  43. empathy_os/socratic/web_ui 2.py +925 -0
  44. empathy_os/tier_recommender.py +5 -2
  45. empathy_os/workflow_commands.py +11 -6
  46. empathy_os/workflows/base.py +1 -1
  47. empathy_os/workflows/batch_processing 2.py +310 -0
  48. empathy_os/workflows/release_prep_crew 2.py +968 -0
  49. empathy_os/workflows/test_coverage_boost_crew 2.py +848 -0
  50. empathy_os/workflows/test_maintenance.py +3 -2
  51. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/entry_points.txt +0 -0
  52. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/licenses/LICENSE +0 -0
  53. {empathy_framework-4.6.2.dist-info → empathy_framework-4.6.3.dist-info}/top_level.txt +0 -0
@@ -215,7 +215,13 @@ class RedisMetrics:
215
215
  return (self.operations_success / self.operations_total) * 100
216
216
 
217
217
  def to_dict(self) -> dict:
218
- """Convert to dictionary for reporting."""
218
+ """Convert metrics to dictionary for reporting and serialization.
219
+
220
+ Returns:
221
+ Dictionary with keys: operations_total, operations_success,
222
+ operations_failed, retries_total, latency_avg_ms, latency_max_ms,
223
+ success_rate, by_operation, security.
224
+ """
219
225
  return {
220
226
  "operations_total": self.operations_total,
221
227
  "operations_success": self.operations_success,
@@ -334,6 +340,12 @@ class StagedPattern:
334
340
  raise TypeError(f"interests must be list, got {type(self.interests).__name__}")
335
341
 
336
342
  def to_dict(self) -> dict:
343
+ """Convert staged pattern to dictionary for serialization.
344
+
345
+ Returns:
346
+ Dictionary with keys: pattern_id, agent_id, pattern_type, name,
347
+ description, code, context, confidence, staged_at, interests.
348
+ """
337
349
  return {
338
350
  "pattern_id": self.pattern_id,
339
351
  "agent_id": self.agent_id,
@@ -349,6 +361,19 @@ class StagedPattern:
349
361
 
350
362
  @classmethod
351
363
  def from_dict(cls, data: dict) -> "StagedPattern":
364
+ """Reconstruct StagedPattern from dictionary.
365
+
366
+ Args:
367
+ data: Dictionary with required keys: pattern_id, agent_id,
368
+ pattern_type, name, description, staged_at.
369
+
370
+ Returns:
371
+ Reconstructed StagedPattern instance.
372
+
373
+ Raises:
374
+ KeyError: If required keys are missing.
375
+ ValueError: If data format is invalid.
376
+ """
352
377
  return cls(
353
378
  pattern_id=data["pattern_id"],
354
379
  agent_id=data["agent_id"],
@@ -382,6 +407,12 @@ class ConflictContext:
382
407
  resolution: str | None = None
383
408
 
384
409
  def to_dict(self) -> dict:
410
+ """Convert conflict context to dictionary for serialization.
411
+
412
+ Returns:
413
+ Dictionary with keys: conflict_id, positions, interests,
414
+ batna, created_at, resolved, resolution.
415
+ """
385
416
  return {
386
417
  "conflict_id": self.conflict_id,
387
418
  "positions": self.positions,
@@ -394,6 +425,19 @@ class ConflictContext:
394
425
 
395
426
  @classmethod
396
427
  def from_dict(cls, data: dict) -> "ConflictContext":
428
+ """Reconstruct ConflictContext from dictionary.
429
+
430
+ Args:
431
+ data: Dictionary with required keys: conflict_id, positions,
432
+ interests, created_at.
433
+
434
+ Returns:
435
+ Reconstructed ConflictContext instance.
436
+
437
+ Raises:
438
+ KeyError: If required keys are missing.
439
+ ValueError: If data format is invalid.
440
+ """
397
441
  return cls(
398
442
  conflict_id=data["conflict_id"],
399
443
  positions=data["positions"],
@@ -0,0 +1,254 @@
1
+ """Dynamic agent creator from templates and form responses.
2
+
3
+ Generates agent specifications based on template rules and user's
4
+ form responses. Core of the meta-workflow system's agent composition.
5
+
6
+ Created: 2026-01-17
7
+ Purpose: Dynamic agent team generation
8
+ """
9
+
10
+ import logging
11
+ from typing import Any
12
+
13
+ from empathy_os.meta_workflows.models import (
14
+ AgentCompositionRule,
15
+ AgentSpec,
16
+ FormResponse,
17
+ MetaWorkflowTemplate,
18
+ TierStrategy,
19
+ )
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class DynamicAgentCreator:
25
+ """Creates agent teams dynamically from templates and form responses.
26
+
27
+ Takes a meta-workflow template and user's form responses, then generates
28
+ a list of agent specifications based on the template's composition rules.
29
+ """
30
+
31
+ def __init__(self):
32
+ """Initialize the dynamic agent creator."""
33
+ self.creation_stats: dict[str, int] = {
34
+ "total_rules_evaluated": 0,
35
+ "agents_created": 0,
36
+ "rules_skipped": 0,
37
+ }
38
+
39
+ def create_agents(
40
+ self, template: MetaWorkflowTemplate, form_response: FormResponse
41
+ ) -> list[AgentSpec]:
42
+ """Create agents from template rules and form responses.
43
+
44
+ Args:
45
+ template: Meta-workflow template with composition rules
46
+ form_response: User's responses to form questions
47
+
48
+ Returns:
49
+ List of AgentSpec instances to execute
50
+
51
+ Raises:
52
+ ValueError: If template or form_response is invalid
53
+ """
54
+ if not template.agent_composition_rules:
55
+ logger.warning(f"Template {template.template_id} has no composition rules")
56
+ return []
57
+
58
+ agents = []
59
+ self.creation_stats["total_rules_evaluated"] = len(
60
+ template.agent_composition_rules
61
+ )
62
+
63
+ logger.info(
64
+ f"Evaluating {len(template.agent_composition_rules)} composition rules"
65
+ )
66
+
67
+ for rule in template.agent_composition_rules:
68
+ # Check if agent should be created based on form responses
69
+ if rule.should_create(form_response):
70
+ agent = self._create_agent_from_rule(rule, form_response)
71
+ agents.append(agent)
72
+ self.creation_stats["agents_created"] += 1
73
+
74
+ logger.debug(
75
+ f"Created agent: {agent.role} "
76
+ f"(tier: {agent.tier_strategy.value}, id: {agent.agent_id})"
77
+ )
78
+ else:
79
+ self.creation_stats["rules_skipped"] += 1
80
+ logger.debug(
81
+ f"Skipped agent {rule.role} - conditions not met"
82
+ )
83
+
84
+ logger.info(
85
+ f"Created {len(agents)} agents from {len(template.agent_composition_rules)} rules"
86
+ )
87
+
88
+ return agents
89
+
90
+ def _create_agent_from_rule(
91
+ self, rule: AgentCompositionRule, form_response: FormResponse
92
+ ) -> AgentSpec:
93
+ """Create an AgentSpec from a composition rule and form responses.
94
+
95
+ Args:
96
+ rule: Agent composition rule
97
+ form_response: User's form responses
98
+
99
+ Returns:
100
+ AgentSpec instance configured for execution
101
+ """
102
+ # Map config from form responses
103
+ config = rule.create_agent_config(form_response)
104
+
105
+ # Create agent spec
106
+ agent = AgentSpec(
107
+ role=rule.role,
108
+ base_template=rule.base_template,
109
+ tier_strategy=rule.tier_strategy,
110
+ tools=rule.tools.copy(), # Copy to avoid mutation
111
+ config=config,
112
+ success_criteria=rule.success_criteria.copy(), # Copy to avoid mutation
113
+ )
114
+
115
+ return agent
116
+
117
+ def get_creation_stats(self) -> dict[str, int]:
118
+ """Get statistics about agent creation.
119
+
120
+ Returns:
121
+ Dictionary with creation statistics
122
+ """
123
+ return self.creation_stats.copy()
124
+
125
+ def reset_stats(self) -> None:
126
+ """Reset creation statistics."""
127
+ self.creation_stats = {
128
+ "total_rules_evaluated": 0,
129
+ "agents_created": 0,
130
+ "rules_skipped": 0,
131
+ }
132
+ logger.debug("Agent creation stats reset")
133
+
134
+
135
+ # =============================================================================
136
+ # Helper functions for agent composition
137
+ # =============================================================================
138
+
139
+
140
+ def group_agents_by_tier_strategy(
141
+ agents: list[AgentSpec],
142
+ ) -> dict[TierStrategy, list[AgentSpec]]:
143
+ """Group agents by their tier strategy.
144
+
145
+ Useful for execution planning (e.g., run all cheap_only agents first).
146
+
147
+ Args:
148
+ agents: List of agent specs
149
+
150
+ Returns:
151
+ Dictionary mapping TierStrategy → list of agents
152
+
153
+ Example:
154
+ >>> agents = [
155
+ ... AgentSpec(role="test", base_template="generic", tier_strategy=TierStrategy.CHEAP_ONLY),
156
+ ... AgentSpec(role="review", base_template="generic", tier_strategy=TierStrategy.PROGRESSIVE),
157
+ ... ]
158
+ >>> grouped = group_agents_by_tier_strategy(agents)
159
+ >>> len(grouped[TierStrategy.CHEAP_ONLY])
160
+ 1
161
+ """
162
+ grouped: dict[TierStrategy, list[AgentSpec]] = {}
163
+
164
+ for agent in agents:
165
+ if agent.tier_strategy not in grouped:
166
+ grouped[agent.tier_strategy] = []
167
+ grouped[agent.tier_strategy].append(agent)
168
+
169
+ return grouped
170
+
171
+
172
+ def estimate_agent_costs(
173
+ agents: list[AgentSpec], cost_per_tier: dict[str, float] | None = None
174
+ ) -> dict[str, Any]:
175
+ """Estimate total cost for executing agents.
176
+
177
+ Args:
178
+ agents: List of agent specs
179
+ cost_per_tier: Optional cost mapping (tier → estimated cost)
180
+ Defaults to reasonable estimates
181
+
182
+ Returns:
183
+ Dictionary with cost estimates
184
+
185
+ Example:
186
+ >>> agents = [AgentSpec(role="test", base_template="generic", tier_strategy=TierStrategy.CHEAP_ONLY)]
187
+ >>> estimate_agent_costs(agents)
188
+ {'total_estimated_cost': 0.05, 'by_tier': {'cheap_only': 0.05}, 'agent_count': 1}
189
+ """
190
+ if cost_per_tier is None:
191
+ # Default cost estimates per tier strategy
192
+ cost_per_tier = {
193
+ "cheap_only": 0.05,
194
+ "progressive": 0.15, # Average, might escalate
195
+ "capable_first": 0.25, # Starts higher
196
+ "premium_only": 0.40,
197
+ }
198
+
199
+ total_cost = 0.0
200
+ cost_by_tier: dict[str, float] = {}
201
+
202
+ for agent in agents:
203
+ tier_key = agent.tier_strategy.value
204
+ agent_cost = cost_per_tier.get(tier_key, 0.10) # Default fallback
205
+
206
+ total_cost += agent_cost
207
+
208
+ if tier_key not in cost_by_tier:
209
+ cost_by_tier[tier_key] = 0.0
210
+ cost_by_tier[tier_key] += agent_cost
211
+
212
+ return {
213
+ "total_estimated_cost": round(total_cost, 2),
214
+ "by_tier": {k: round(v, 2) for k, v in cost_by_tier.items()},
215
+ "agent_count": len(agents),
216
+ }
217
+
218
+
219
+ def validate_agent_dependencies(agents: list[AgentSpec]) -> list[str]:
220
+ """Validate that agent dependencies are satisfied.
221
+
222
+ Checks for common dependency issues (e.g., publisher needs package_builder).
223
+
224
+ Args:
225
+ agents: List of agent specs
226
+
227
+ Returns:
228
+ List of validation warnings (empty if all OK)
229
+
230
+ Example:
231
+ >>> agents = [AgentSpec(role="publisher", base_template="generic", tier_strategy=TierStrategy.CHEAP_ONLY)]
232
+ >>> warnings = validate_agent_dependencies(agents)
233
+ >>> len(warnings) > 0 # Should warn about missing package_builder
234
+ True
235
+ """
236
+ warnings = []
237
+ agent_roles = {agent.role for agent in agents}
238
+
239
+ # Common dependencies
240
+ dependencies = {
241
+ "publisher": ["package_builder"],
242
+ "changelog_updater": ["version_manager"],
243
+ }
244
+
245
+ for agent in agents:
246
+ if agent.role in dependencies:
247
+ for required_role in dependencies[agent.role]:
248
+ if required_role not in agent_roles:
249
+ warnings.append(
250
+ f"Agent '{agent.role}' typically requires '{required_role}' "
251
+ f"but it's not in the agent list"
252
+ )
253
+
254
+ return warnings