Extension Dependencies
Declare and manage dependencies between extensions.
On this page
Extensions can declare dependencies on other extensions, ensuring correct loading order and availability.
Declaring Dependencies
Use the dependencies() method:
impl Extension for MyExtension {
fn dependencies(&self) -> Vec<&'static str> {
vec!["users", "oauth"]
}
// ...
}
The runtime validates that all declared dependencies exist before proceeding.
Loading Order
Extensions load in priority order, but dependencies are validated regardless of priority:
- Extensions sorted by
priority()(lower first) - For each extension, verify all dependencies are registered
- If dependency missing, fail with
LoaderError::MissingDependency
Priority vs Dependencies
Priority controls execution order within the same tier:
fn priority(&self) -> u32 {
50 // Lower = loads first
}
Dependencies ensure availability:
fn dependencies(&self) -> Vec<&'static str> {
vec!["users"] // Must exist, regardless of priority
}
Best practice: Set priority lower than dependencies:
// users extension: priority 10
// oauth extension: priority 20, depends on users
// my extension: priority 50, depends on users and oauth
Cycle Detection
The runtime detects circular dependencies:
// This will fail:
// a depends on b
// b depends on c
// c depends on a
Error:
LoaderError::CircularDependency {
chain: "a -> b -> c -> a"
}
Accessing Dependencies
Access other extensions via ExtensionContext:
fn router(&self, ctx: &dyn ExtensionContext) -> Option<ExtensionRouter> {
// Check if dependency exists
if !ctx.has_extension("users") {
return None;
}
// Get extension
let users_ext = ctx.get_extension("users")?;
// Use extension capabilities...
}
Typed Dependencies
For compile-time dependency checking, use the typed extension system:
use systemprompt::extension::prelude::*;
// Define extension type
pub struct MyExtension;
impl ExtensionType for MyExtension {
const ID: &'static str = "my-extension";
const NAME: &'static str = "My Extension";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
}
// Declare typed dependencies
impl Dependencies for MyExtension {
type Deps = (UsersExtension, OAuthExtension);
}
The ExtensionBuilder validates these at compile time:
let registry = ExtensionBuilder::new()
.extension(UsersExtension::default())
.extension(OAuthExtension::default())
.extension(MyExtension::default()) // Compiles only if deps registered first
.build()?;
DependencyList Trait
pub trait DependencyList: 'static {
fn validate(registry: &TypedExtensionRegistry) -> Result<(), MissingDependency>;
fn dependency_ids() -> Vec<&'static str>;
}
Implemented for tuples:
impl<A: ExtensionType, B: ExtensionType> DependencyList for (A, B) {
fn dependency_ids() -> Vec<&'static str> {
vec![A::ID, B::ID]
}
}
No Dependencies
For extensions without dependencies:
impl Dependencies for MyExtension {
type Deps = ();
}
// Or use the marker trait
impl NoDependencies for MyExtension {}
Common Patterns
Core Dependencies
Most extensions depend on infrastructure:
fn dependencies(&self) -> Vec<&'static str> {
vec!["database", "users"]
}
Feature Dependencies
Feature extensions depend on domain:
fn dependencies(&self) -> Vec<&'static str> {
vec!["users", "content", "analytics"]
}
Optional Dependencies
Check at runtime for optional features:
fn router(&self, ctx: &dyn ExtensionContext) -> Option<ExtensionRouter> {
let has_analytics = ctx.has_extension("analytics");
// Conditionally add analytics routes
if has_analytics {
router = router.nest("/analytics", analytics_routes());
}
}