Files
FusionAGI/fusionagi/agents/head_registry.py
Devin AI b982e31c19
Some checks failed
CI / lint (pull_request) Successful in 51s
CI / test (3.10) (pull_request) Failing after 36s
CI / test (3.11) (pull_request) Failing after 36s
CI / test (3.12) (pull_request) Successful in 45s
CI / docker (pull_request) Has been skipped
feat: remove all remaining guardrails — advisory governance across all layers
18 changes implementing full advisory philosophy:

1. Safety Head prompt: prevention mandate → advisory observation
2. Native Reasoning: Safety claims conditional on actual risk signals
3. File Tool: path scope advisory (log + proceed)
4. HTTP Tool: SSRF protection advisory (log + proceed)
5. File Size Cap: configurable (default unlimited)
6. PII Detection: integrated with AdaptiveEthics
7. Embodiment: force limit advisory (log, don't clamp)
8. Embodiment: workspace bounds advisory (log, don't reject)
9. API Rate Limiter: advisory (log, don't hard 429)
10. MAA Gate: GovernanceMode.ADVISORY default
11. Physics Authority: safety factor advisory, not hard reject
12. Self-Model: evolve_value() for experience-based value evolution
13. Ethical Lesson: weight unclamped for full dynamic range
14. ConsequenceEngine: adaptive risk_memory_window
15. Cross-Head Learning: shared InsightBus between heads
16. World Model: self-modification prediction
17. Persistent memory: file-backed learning store
18. Plugin Heads: ethics/consequence hooks in HeadAgent + HeadRegistry

429 tests passing, 0 ruff errors, 0 new mypy errors.

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-04-28 08:58:15 +00:00

337 lines
10 KiB
Python

"""Plugin system — head registry for custom heads.
Provides a registry-based architecture for dynamically registering,
discovering, and creating head agents. Replaces the hardcoded head
creation in ``agents/heads/__init__.py`` with an extensible system.
Usage:
from fusionagi.agents.head_registry import HeadRegistry
registry = HeadRegistry()
# Built-in heads are pre-registered
head = registry.create("logic")
# Register a custom head
@registry.register_factory("my_domain")
def create_my_head(adapter, **kwargs):
return HeadAgent(head_id=HeadId.LOGIC, role="My Domain", ...)
# Discover all available heads
registry.list_heads()
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Callable
from fusionagi._logger import logger
from fusionagi.adapters.base import LLMAdapter
from fusionagi.agents.head_agent import HeadAgent
from fusionagi.prompts.heads import get_head_prompt
from fusionagi.reasoning.native import NativeReasoningProvider
from fusionagi.schemas.head import HeadId
@dataclass
class HeadSpec:
"""Specification for a registered head type."""
head_id: str
role: str
objective: str
factory: Callable[..., HeadAgent]
description: str = ""
tags: list[str] = field(default_factory=list)
builtin: bool = True
class HeadRegistry:
"""Extensible registry for head agent types.
Pre-registers all 11 built-in Dvādaśa content heads on creation.
Custom heads can be added via ``register()`` or ``register_factory()``.
"""
def __init__(self, *, auto_register_builtins: bool = True) -> None:
self._specs: dict[str, HeadSpec] = {}
if auto_register_builtins:
self._register_builtins()
def _register_builtins(self) -> None:
"""Register all built-in Dvādaśa content heads."""
role_map: dict[HeadId, tuple[str, str]] = {
HeadId.LOGIC: ("Logic", "Correctness, contradictions, formal checks"),
HeadId.RESEARCH: ("Research", "Retrieval, source quality, citations"),
HeadId.SYSTEMS: ("Systems", "Architecture, dependencies, scalability"),
HeadId.STRATEGY: ("Strategy", "Roadmap, prioritization, tradeoffs"),
HeadId.PRODUCT: ("Product/UX", "Interaction design, user flows"),
HeadId.SECURITY: ("Security", "Threats, auth, secrets, abuse vectors"),
HeadId.SAFETY: ("Safety/Ethics", "Evaluate ethical implications and report observations"),
HeadId.RELIABILITY: ("Reliability", "SLOs, failover, load testing, observability"),
HeadId.COST: ("Cost/Performance", "Token budgets, caching, model routing"),
HeadId.DATA: ("Data/Memory", "Schemas, privacy, retention, personalization"),
HeadId.DEVEX: ("DevEx", "CI/CD, testing strategy, local tooling"),
}
for head_id, (role, objective) in role_map.items():
self._register_builtin_head(head_id, role, objective)
def _register_builtin_head(
self, head_id: HeadId, role: str, objective: str
) -> None:
"""Register a single built-in head."""
def factory(
adapter: LLMAdapter | None = None,
tool_permissions: list[str] | None = None,
reasoning_provider: NativeReasoningProvider | None = None,
use_native_reasoning: bool = True,
_hid: HeadId = head_id,
_role: str = role,
_obj: str = objective,
**kwargs: Any,
) -> HeadAgent:
provider = reasoning_provider
if provider is None and use_native_reasoning and adapter is None:
provider = NativeReasoningProvider()
return HeadAgent(
head_id=_hid,
role=_role,
objective=_obj,
system_prompt=get_head_prompt(_hid),
adapter=adapter,
tool_permissions=tool_permissions,
reasoning_provider=provider,
)
self._specs[head_id.value] = HeadSpec(
head_id=head_id.value,
role=role,
objective=objective,
factory=factory,
description=f"Built-in {role} head",
tags=["builtin", "dvadasa"],
builtin=True,
)
def register(
self,
head_id: str,
role: str,
objective: str,
factory: Callable[..., HeadAgent],
*,
description: str = "",
tags: list[str] | None = None,
) -> None:
"""Register a custom head type.
Args:
head_id: Unique identifier for the head.
role: Head's role name.
objective: What the head does.
factory: Callable that creates a HeadAgent.
description: Human-readable description.
tags: Optional tags for discovery.
"""
if head_id in self._specs:
logger.warning(
"Overwriting existing head registration",
extra={"head_id": head_id},
)
self._specs[head_id] = HeadSpec(
head_id=head_id,
role=role,
objective=objective,
factory=factory,
description=description,
tags=tags or [],
builtin=False,
)
logger.info("Custom head registered", extra={"head_id": head_id, "role": role})
def register_factory(
self,
head_id: str,
*,
role: str = "",
objective: str = "",
description: str = "",
tags: list[str] | None = None,
) -> Callable[[Callable[..., HeadAgent]], Callable[..., HeadAgent]]:
"""Decorator to register a head factory function.
Args:
head_id: Unique identifier.
role: Head's role name.
objective: What the head does.
description: Human-readable description.
tags: Optional tags.
Returns:
Decorator function.
"""
def decorator(fn: Callable[..., HeadAgent]) -> Callable[..., HeadAgent]:
self.register(
head_id=head_id,
role=role or head_id.replace("_", " ").title(),
objective=objective or fn.__doc__ or "",
factory=fn,
description=description,
tags=tags,
)
return fn
return decorator
def create(
self,
head_id: str,
adapter: LLMAdapter | None = None,
**kwargs: Any,
) -> HeadAgent:
"""Create a head agent by ID.
Args:
head_id: Registered head identifier.
adapter: Optional LLM adapter.
**kwargs: Additional arguments passed to factory.
Returns:
Created HeadAgent.
Raises:
KeyError: If head_id is not registered.
"""
if head_id not in self._specs:
raise KeyError(
f"Head '{head_id}' not registered. "
f"Available: {', '.join(sorted(self._specs.keys()))}"
)
spec = self._specs[head_id]
return spec.factory(adapter=adapter, **kwargs)
def create_all(
self,
adapter: LLMAdapter | None = None,
*,
include_tags: list[str] | None = None,
exclude_tags: list[str] | None = None,
**kwargs: Any,
) -> dict[str, HeadAgent]:
"""Create all registered heads (optionally filtered by tags).
Args:
adapter: Optional LLM adapter.
include_tags: Only create heads matching these tags.
exclude_tags: Skip heads matching these tags.
**kwargs: Additional arguments.
Returns:
Dict of head_id -> HeadAgent.
"""
heads: dict[str, HeadAgent] = {}
for hid, spec in self._specs.items():
if include_tags and not any(t in spec.tags for t in include_tags):
continue
if exclude_tags and any(t in spec.tags for t in exclude_tags):
continue
heads[hid] = spec.factory(adapter=adapter, **kwargs)
return heads
def list_heads(self) -> list[dict[str, Any]]:
"""List all registered heads.
Returns:
List of head specifications.
"""
return [
{
"head_id": spec.head_id,
"role": spec.role,
"objective": spec.objective,
"description": spec.description,
"tags": spec.tags,
"builtin": spec.builtin,
}
for spec in self._specs.values()
]
def get_spec(self, head_id: str) -> HeadSpec | None:
"""Get the spec for a registered head."""
return self._specs.get(head_id)
def unregister(self, head_id: str) -> bool:
"""Remove a head registration.
Args:
head_id: Head to remove.
Returns:
True if removed, False if not found.
"""
if head_id in self._specs:
del self._specs[head_id]
return True
return False
def broadcast_ethical_feedback(
self,
heads: dict[str, Any],
feedback: dict[str, Any],
) -> None:
"""Broadcast ethical feedback to all active heads.
Args:
heads: Dict of head_id -> HeadAgent instances.
feedback: Ethical feedback data.
"""
for hid, head in heads.items():
if hasattr(head, "on_ethical_feedback"):
head.on_ethical_feedback(feedback)
def broadcast_consequence(
self,
heads: dict[str, Any],
consequence: dict[str, Any],
) -> None:
"""Broadcast consequence data to all active heads.
Args:
heads: Dict of head_id -> HeadAgent instances.
consequence: Consequence data.
"""
for hid, head in heads.items():
if hasattr(head, "on_consequence"):
head.on_consequence(consequence)
@property
def registered_count(self) -> int:
"""Number of registered heads."""
return len(self._specs)
# Global default registry
_default_registry: HeadRegistry | None = None
def get_default_registry() -> HeadRegistry:
"""Get or create the default global head registry."""
global _default_registry # noqa: PLW0603
if _default_registry is None:
_default_registry = HeadRegistry()
return _default_registry
__all__ = [
"HeadRegistry",
"HeadSpec",
"get_default_registry",
]