Cross-Plugin Orchestration¶
As the FCC ecosystem grows beyond the core framework, plugins from different packages need to coordinate. The CrossPluginOrchestrator manages dependencies between plugins, tracks persona interactions across plugin boundaries, and monitors ecosystem health. This tutorial explains the orchestration model and shows how to use it.
The Plugin Dependency Model¶
FCC plugins can depend on each other in three ways:
| Dependency Type | Description | Example |
|---|---|---|
provides_llm |
One plugin supplies LLM capabilities to another | PAOM provides LLM access to FCC |
provides_taxonomy |
One plugin supplies classification structures | CONSTEL provides taxonomy to AOME |
provides_metadata |
One plugin supplies metadata or configuration | FCC provides persona specs to PAOM |
These dependencies form a directed graph. The CrossPluginOrchestrator lets you declare, query, and validate this graph.
Setting Up the Orchestrator¶
from fcc.plugins.orchestration import (
CrossPluginOrchestrator,
PluginDependency,
PluginInteraction,
PluginHealthStatus,
)
# Declare plugin dependencies
dependencies = [
PluginDependency(
source_plugin="paom",
target_plugin="fcc-core",
dependency_type="provides_metadata",
description="PAOM consumes FCC persona specifications",
),
PluginDependency(
source_plugin="aome",
target_plugin="constel",
dependency_type="provides_taxonomy",
description="AOME uses CONSTEL taxonomy for archetype classification",
),
PluginDependency(
source_plugin="constel",
target_plugin="fcc-core",
dependency_type="provides_metadata",
description="CONSTEL reads persona dimension profiles from FCC",
),
]
orchestrator = CrossPluginOrchestrator(dependencies=dependencies)
print(f"Registered dependencies: {orchestrator.dependency_count}")
Resolving Dependencies¶
Query what a plugin depends on (forward dependencies) or what depends on it (reverse dependencies):
# What does PAOM depend on?
paom_deps = orchestrator.resolve_dependencies("paom")
for dep in paom_deps:
print(f" PAOM -> {dep.target_plugin} ({dep.dependency_type})")
# What depends on fcc-core?
fcc_consumers = orchestrator.reverse_dependencies("fcc-core")
for dep in fcc_consumers:
print(f" {dep.source_plugin} -> fcc-core ({dep.dependency_type})")
Expected output:
PAOM -> fcc-core (provides_metadata)
paom -> fcc-core (provides_metadata)
constel -> fcc-core (provides_metadata)
Cross-Plugin Persona Interactions¶
Personas from different plugins can interact. The orchestrator tracks these cross-boundary interactions:
# Define cross-plugin persona interactions
interactions = [
PluginInteraction(
source_persona="RC",
source_plugin="fcc-core",
target_persona="PAOM-RA",
target_plugin="paom",
interaction_type="downstream",
description="Research Crafter feeds findings to PAOM Research Analyst",
),
PluginInteraction(
source_persona="STE",
source_plugin="fcc-core",
target_persona="CONSTEL-TE",
target_plugin="constel",
interaction_type="peer",
description="Taxonomy engineers coordinate classification standards",
),
PluginInteraction(
source_persona="PAOM-RA",
source_plugin="paom",
target_persona="AOME-AA",
target_plugin="aome",
interaction_type="downstream",
description="PAOM analysis feeds AOME archetype assessment",
),
]
# Add interactions to the orchestrator
for interaction in interactions:
orchestrator.add_interaction(interaction)
print(f"Total interactions: {orchestrator.interaction_count}")
Querying the Interaction Matrix¶
The interaction matrix groups interactions by source plugin for a bird's-eye view:
matrix = orchestrator.get_interaction_matrix()
for plugin_id, plugin_interactions in matrix.items():
print(f"\n{plugin_id}:")
for ix in plugin_interactions:
print(f" {ix.source_persona} -> {ix.target_persona} "
f"({ix.interaction_type})")
Expected output:
You can also query interactions for a specific persona:
# Find all interactions involving the Research Crafter
rc_interactions = orchestrator.get_interactions_for_persona("RC")
for ix in rc_interactions:
direction = "sends to" if ix.source_persona == "RC" else "receives from"
other = ix.target_persona if ix.source_persona == "RC" else ix.source_persona
print(f" RC {direction} {other} ({ix.interaction_type})")
Validating Interactions¶
The orchestrator can validate that all interactions reference known plugins:
errors = orchestrator.validate_interactions()
if errors:
for err in errors:
print(f" Validation error: {err}")
else:
print("All interactions are valid")
Ecosystem Health Monitoring¶
The orchestrator aggregates health status from individual plugins into an ecosystem-wide report:
# Collect health status from each plugin
statuses = [
PluginHealthStatus(
plugin_id="fcc-core",
healthy=True,
persona_count=84,
),
PluginHealthStatus(
plugin_id="paom",
healthy=True,
persona_count=12,
),
PluginHealthStatus(
plugin_id="aome",
healthy=False,
persona_count=8,
error="Missing taxonomy dependency",
),
]
report = orchestrator.check_health(statuses)
print(f"Total plugins: {report.total_plugins}")
print(f"Healthy: {report.healthy_plugins}")
print(f"Unhealthy: {report.unhealthy_plugins}")
print(f"Total personas: {report.total_personas}")
# Drill into unhealthy plugins
for status in report.statuses:
if not status.healthy:
print(f"\n Plugin {status.plugin_id}: {status.error}")
Expected output:
Total plugins: 3
Healthy: 2
Unhealthy: 1
Total personas: 104
Plugin aome: Missing taxonomy dependency
The Plugin Registry¶
The PluginRegistry discovers plugins via Python entry points. It scans all fcc.plugins.* entry-point groups at startup:
from fcc.plugins.registry import PluginRegistry
from fcc.plugins.base import PluginType
registry = PluginRegistry()
result = registry.discover()
print(f"Discovered: {result.discovered}")
print(f"Loaded: {result.loaded}")
if result.errors:
for err in result.errors:
print(f" Error: {err}")
# List plugins by type
for plugin_type_name, count in registry.plugins_by_type().items():
if count > 0:
print(f" {plugin_type_name}: {count}")
Next Steps¶
- Building Your First FCC Plugin -- Create a plugin package
- Managing a Plugin Ecosystem -- Ecosystem registry and ports
- Creating Event Subscribers -- The 10th plugin type