How Switchyard RuntimeModels Differentiates Parent and Sub-Agent Model Groups
Switchyard's RuntimeModels struct maintains two isolated HashMap collections—by_category for parent agents and an optional subagent map for delegated agents—enabling complete separation of model groups through distinct accessor methods.
The RuntimeModels container serves as the central registry that routing algorithms use to look up available model IDs during request processing. According to the NVIDIA-NeMo/Switchyard source code, this Rust-based orchestration system implements strict capability isolation by storing parent and sub-agent model groups in separate struct fields with scope-aware lookup methods.
Core Data Structure in algorithm.rs
The RuntimeModels definition resides in crates/libsy/src/core/algorithm.rs at lines 46-52, where the struct explicitly partitions model visibility into two distinct storage fields:
pub struct RuntimeModels {
/// When using subagents this is the parent agent category.
by_category: HashMap<Category, Vec<ModelId>>,
subagent: Option<HashMap<Category, Vec<ModelId>>>,
}
This architecture enforces complete isolation between execution contexts:
by_category– AHashMap<Category, Vec<ModelId>>containing model groups exclusively available to the main (parent) agentsubagent– AnOption<HashMap<Category, Vec<ModelId>>>holding an optional, secondary set of groups visible only to delegated sub-agents
Parent Model Access with models_for()
Parent agents retrieve their available models through the models_for() method implemented at lines 68-71 of crates/libsy/src/core/algorithm.rs:
pub fn models_for(&self, category: &Category) -> &[ModelId] {
self.by_category.get(category).map_or(&[], Vec::as_slice)
}
This method returns a slice reference (&[ModelId]) directly from the by_category HashMap. When the requested category does not exist in the parent collection, it returns an empty slice rather than exposing sub-agent models as a fallback.
Sub-Agent Model Access with subagent_models_for()
Sub-agents access their dedicated model groups via subagent_models_for(), defined at lines 73-78 in the same file:
pub fn subagent_models_for(&self, category: &Category) -> &[ModelId] {
self.subagent
.as_ref()
.and_then(|models| models.get(category))
.map_or(&[], Vec::as_slice)
}
This accessor traverses the optional subagent field using as_ref() and and_then() to safely handle cases where no sub-agent map exists. Like its parent counterpart, it returns an empty slice for missing categories, ensuring sub-agents never accidentally inherit parent model visibility.
Driver Scope Selection Logic
The routing Driver determines which collection to query based on its execution scope. The source defines a Scope enum with Parent and Subagent variants:
- Parent scope: The main driver calls
models.models_for(category)to read exclusively from theby_categoryfield - Subagent scope: When spawning sub-agents via
Driver::for_subagent, the cloned driver switches toScope::Subagentand invokesmodels.subagent_models_for(category)
This scope-based dispatch ensures that parent agents cannot access sub-agent model groups, and sub-agents cannot fall back to parent models unless developers explicitly populate both HashMaps with identical data during RuntimeModels construction.
Practical Implementation Example
The following example demonstrates constructing a RuntimeModels instance with both parent and sub-agent collections, then accessing each group through its respective method:
use switchyard_libsy::RuntimeModels;
use switchyard_protocol::{Category, ModelId};
use std::collections::HashMap;
// Build RuntimeModels with both parent and sub-agent groups
let mut parent = HashMap::new();
parent.insert(Category::Efficient, vec![ModelId::from("fast-model")]);
let mut sub = HashMap::new();
sub.insert(Category::Efficient, vec![ModelId::from("sub-fast-model")]);
let runtime = RuntimeModels::new(parent).with_subagent(sub);
// Parent driver reads from by_category
let parent_models = runtime.models_for(&Category::Efficient);
// => returns ["fast-model"]
// Sub-agent driver reads from subagent option
let sub_models = runtime.subagent_models_for(&Category::Efficient);
// => returns ["sub-fast-model"]
In crates/libsy/src/algorithms/subagent.rs, you can find production examples of this pattern where sub-agents receive restricted model selections distinct from their parent agents' capabilities.
Summary
- Dual-storage architecture:
RuntimeModelsuses separateby_categoryandsubagentHashMap fields to physically isolate parent and sub-agent model groups - Dedicated accessors: The
models_for()andsubagent_models_for()methods provide clean, type-safe APIs that prevent cross-contamination between agent scopes - Scope-driven selection: The
Driverenum (ParentvsSubagent) determines which accessor to call, ensuring runtime enforcement of model visibility boundaries - Safe defaults: Both accessors return empty slices for missing categories rather than falling back to alternative collections, maintaining strict isolation by default
Frequently Asked Questions
What happens if subagent_models_for is called but no sub-agent map exists?
The method returns an empty slice (&[]). Because the subagent field is an Option<HashMap>, the implementation uses as_ref() and and_then() to safely handle the None case, mapping it to an empty slice without panicking or falling back to parent models.
Can parent agents access sub-agent model groups?
No. The models_for() method only queries the by_category HashMap and has no visibility into the subagent optional field. This architectural separation in crates/libsy/src/core/algorithm.rs ensures parent agents remain unaware of sub-agent capabilities unless both collections are explicitly populated with overlapping model IDs during construction.
How does the Driver switch between parent and sub-agent scopes?
The Driver implements a cloning mechanism via for_subagent() that creates a new driver instance with Scope::Subagent. When the routing algorithm invokes model lookups, this scope determines whether to call models_for() (parent scope) or subagent_models_for() (sub-agent scope), effectively switching the visible model group without modifying the underlying RuntimeModels struct.
Where is RuntimeModels typically instantiated in the Switchyard codebase?
Production instances are commonly built in crates/libsy/src/algorithms/util/target_selector.rs, where the target selection logic constructs RuntimeModels by mapping categories to model IDs based on configuration. The sub-agent variant sees heavy use in crates/libsy/src/algorithms/subagent.rs for scenarios requiring delegated agent isolation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →