feat: complete phase 2 -- multi-agent routing, interrupt TTL, escalation, templates

- Intent classification with LLM structured output (single/multi/ambiguous)
- Discount agent with apply_discount and generate_coupon tools
- Interrupt manager with 30-min TTL auto-expiration and retry prompts
- Webhook escalation module with exponential backoff retry (max 3)
- Three vertical industry templates (e-commerce, SaaS, fintech)
- Template loading in AgentRegistry
- Enhanced supervisor prompt with dynamic agent descriptions
- 153 tests passing, 90.18% coverage
This commit is contained in:
Yaojia Wang
2026-03-30 21:04:39 +02:00
parent 7c3571b47d
commit 1050df780d
27 changed files with 1683 additions and 43 deletions

View File

@@ -100,5 +100,41 @@ class AgentRegistry:
def get_agents_by_permission(self, permission: str) -> tuple[AgentConfig, ...]:
return tuple(a for a in self._agents.values() if a.permission == permission)
@classmethod
def load_template(
cls,
template_name: str,
templates_dir: str | Path | None = None,
) -> AgentRegistry:
"""Load agent configurations from a named template."""
if templates_dir is None:
templates_dir = Path(__file__).parent.parent / "templates"
templates_dir = Path(templates_dir)
yaml_path = templates_dir / f"{template_name}.yaml"
if not yaml_path.exists():
available = cls.list_templates(templates_dir)
raise FileNotFoundError(
f"Template '{template_name}' not found. "
f"Available: {', '.join(available) if available else 'none'}"
)
return cls.load(yaml_path)
@classmethod
def list_templates(
cls,
templates_dir: str | Path | None = None,
) -> tuple[str, ...]:
"""List available template names from the templates directory."""
if templates_dir is None:
templates_dir = Path(__file__).parent.parent / "templates"
templates_dir = Path(templates_dir)
if not templates_dir.is_dir():
return ()
return tuple(
sorted(p.stem for p in templates_dir.glob("*.yaml"))
)
def __len__(self) -> int:
return len(self._agents)