| Crates.io | herosal-mycelium |
| lib.rs | herosal-mycelium |
| version | 0.1.4 |
| created_at | 2025-12-13 06:10:01.87086+00 |
| updated_at | 2025-12-15 04:36:22.481077+00 |
| description | Mycelium - Distributed infrastructure components for GeoMind |
| homepage | |
| repository | https://github.com/threefoldtech/sal |
| max_upload_size | |
| id | 1982607 |
| size | 234,460 |
sal-capacitymodel)Node specification and capacity planning for GeoMind/Mycelium.
Add this to your Cargo.toml:
[dependencies]
sal-capacitymodel = { git = "https://forge.ourworld.tf/geomind_research/herolib_rust.git", package = "sal-capacitymodel" }
Then in your Rust code:
use sal_capacitymodel::{NodeSpec, HardwareSpec, CapacitySpec, AgentSpec, SliceSpec, PricingSpec};
This crate provides data structures for defining node specifications including hardware, capacity, agents, slices, and pricing information. It supports both TOML and HeroScript serialization formats.
use sal_capacitymodel::*;
let node = NodeSpec {
name: "ryzen_ai_max_395".into(),
hardware: HardwareSpec {
hardware_cost_usd: 4200.0,
power_consumption_watts: 180.0,
rack_units: 2.0,
},
capacity: CapacitySpec {
memory_gb: 128,
vcpu: 32,
ssd_gb: 2000,
hdd_gb: 0,
nr_gpu: 0,
passmark: 50000,
ai_tps_performance_70b: 85000,
},
agents: AgentSpec {
max_agents: 16,
agent_type: "pro".into(),
},
slices: SliceSpec { nr_slices: 8 },
pricing: PricingSpec {
node_price_per_slice_per_month: 0.12,
agent_price_per_month: 0.03,
hdd_price_per_gb_per_month: 0.002,
},
};
let toml_str = node.to_toml().unwrap();
println!("{}", toml_str);
Output:
name = "ryzen_ai_max_395"
[hardware]
hardware_cost_usd = 4200.0
power_consumption_watts = 180.0
rack_units = 2.0
[capacity]
memory_gb = 128
vcpu = 32
ssd_gb = 2000
hdd_gb = 0
nr_gpu = 0
passmark = 50000
ai_tps_performance_70b = 85000
[agents]
max_agents = 16
agent_type = "pro"
[slices]
nr_slices = 8
[pricing]
node_price_per_slice_per_month = 0.12
agent_price_per_month = 0.03
hdd_price_per_gb_per_month = 0.002
use sal_capacitymodel::NodeSpec;
let toml_str = r#"
name = "parsed_node"
[hardware]
hardware_cost_usd = 5000.0
power_consumption_watts = 200.0
rack_units = 2.0
[capacity]
memory_gb = 256
vcpu = 64
ssd_gb = 4000
hdd_gb = 8000
nr_gpu = 2
passmark = 100000
ai_tps_performance_70b = 150000
[agents]
max_agents = 32
agent_type = "enterprise"
[slices]
nr_slices = 16
[pricing]
node_price_per_slice_per_month = 0.25
agent_price_per_month = 0.05
hdd_price_per_gb_per_month = 0.003
"#;
let node = NodeSpec::from_toml(toml_str).unwrap();
assert_eq!(node.name, "parsed_node");
assert_eq!(node.capacity.memory_gb, 256);
use sal_capacitymodel::NodeSpec;
let script = r#"
!!nodespec.define
name: 'ryzen_ai_max_395'
hardware_cost_usd: 4200.0
power_consumption_watts: 180.0
rack_units: 2.0
memory_gb: 128
vcpu: 32
ssd_gb: 2000
hdd_gb: 0
nr_gpu: 0
passmark: 50000
ai_tps_performance_70b: 85000
max_agents: 16
agent_type: 'pro'
nr_slices: 8
node_price_per_slice_per_month: 0.12
agent_price_per_month: 0.03
hdd_price_per_gb_per_month: 0.002
"#;
let nodes = NodeSpec::from_heroscript(script).unwrap();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].name, "ryzen_ai_max_395");
let script = r#"
!!nodespec.define
name: 'node_small'
memory_gb: 64
vcpu: 16
nr_slices: 4
!!nodespec.define
name: 'node_large'
memory_gb: 256
vcpu: 64
nr_slices: 16
"#;
let nodes = NodeSpec::from_heroscript(script).unwrap();
assert_eq!(nodes.len(), 2);
let heroscript = node.to_heroscript();
println!("{}", heroscript);
Output:
!!nodespec.define
name: 'ryzen_ai_max_395'
hardware_cost_usd: 4200
power_consumption_watts: 180
rack_units: 2
memory_gb: 128
vcpu: 32
ssd_gb: 2000
hdd_gb: 0
nr_gpu: 0
passmark: 50000
ai_tps_performance_70b: 85000
max_agents: 16
agent_type: 'pro'
nr_slices: 8
node_price_per_slice_per_month: 0.12
agent_price_per_month: 0.03
hdd_price_per_gb_per_month: 0.002
The main structure containing all node specifications:
| Field | Type | Description |
|---|---|---|
name |
String |
Unique identifier (lowercase, no spaces, ASCII) |
hardware |
HardwareSpec |
Physical hardware specifications |
capacity |
CapacitySpec |
Compute and storage capacity |
agents |
AgentSpec |
Agent configuration |
slices |
SliceSpec |
Slice configuration |
pricing |
PricingSpec |
Pricing information |
| Field | Type | Description |
|---|---|---|
hardware_cost_usd |
f32 |
One-time CAPEX cost in USD |
power_consumption_watts |
f32 |
Average power draw in watts |
rack_units |
f32 |
Number of rack units (U) |
| Field | Type | Description |
|---|---|---|
memory_gb |
u32 |
Total RAM in GB |
vcpu |
u32 |
Total virtual CPUs (logical cores) |
ssd_gb |
u32 |
SSD storage in GB |
hdd_gb |
u32 |
HDD storage in GB |
nr_gpu |
u32 |
Number of GPUs |
passmark |
u32 |
CPU benchmark score (PassMark) |
ai_tps_performance_70b |
u32 |
AI performance for 70B model (tokens/sec) |
| Field | Type | Description |
|---|---|---|
max_agents |
u32 |
Maximum agents allowed |
agent_type |
String |
Type of agent (lowercase snake_case) |
| Field | Type | Description |
|---|---|---|
nr_slices |
u32 |
Number of slices provided |
| Field | Type | Description |
|---|---|---|
node_price_per_slice_per_month |
f64 |
Price per slice per month (USD) |
agent_price_per_month |
f64 |
Price per agent per month (USD) |
hdd_price_per_gb_per_month |
f64 |
Price per GB HDD per month (USD) |
When using !!nodespec.define, the following parameters are available:
| Parameter | Required | Default | Description |
|---|---|---|---|
name |
Yes | - | Node identifier |
hardware_cost_usd |
No | 0.0 | Hardware cost |
power_consumption_watts |
No | 0.0 | Power consumption |
rack_units |
No | 1.0 | Rack units |
memory_gb |
No | 0 | RAM in GB |
vcpu |
No | 0 | Virtual CPUs |
ssd_gb |
No | 0 | SSD storage |
hdd_gb |
No | 0 | HDD storage |
nr_gpu |
No | 0 | GPU count |
passmark |
No | 0 | CPU benchmark |
ai_tps_performance_70b |
No | 0 | AI performance |
max_agents |
No | 0 | Max agents |
agent_type |
No | "standard" | Agent type |
nr_slices |
No | 1 | Slice count |
node_price_per_slice_per_month |
No | 0.0 | Slice price |
agent_price_per_month |
No | 0.0 | Agent price |
hdd_price_per_gb_per_month |
No | 0.0 | HDD price |
NodeSpec includes automatic slice calculation based on hardware:
nr_slices = nr_gpu (each GPU gets its own slice)nr_slices = memory_gb / 4 (4 GB per slice)let mut node = NodeSpec::default();
node.capacity.memory_gb = 128;
node.capacity.nr_gpu = 0;
node.auto_configure_slices();
assert_eq!(node.slices.nr_slices, 32); // 128 / 4 = 32 slices
// Total monthly revenue for all slices
let monthly = node.total_monthly_revenue();
// Slice pricing (hourly)
let hourly = node.slice_price_per_hour(); // 2x multiplier for on-demand
let committed = node.slice_price_per_hour_committed(); // No multiplier
// Agent pricing (hourly)
let agent_hourly = node.agent_price_per_hour(); // 2x multiplier
let agent_committed = node.agent_price_per_hour_committed();
Quick creation of common node configurations:
// Standard compute node (64GB/16vCPU, 16 slices)
let compute = NodeSpec::preset_compute();
// AI workstation (128GB/32vCPU/1GPU, 1 slice)
let workstation = NodeSpec::preset_workstation();
// Quad-GPU server (512GB/128vCPU/4GPU, 4 slices)
let server = NodeSpec::preset_server_quatro();
Manage multiple nodes together:
use sal_capacitymodel::{NodeCollection, NodeSpec};
let mut collection = NodeCollection::new();
// Add nodes
collection.add(NodeSpec::preset_compute());
collection.add(NodeSpec::preset_workstation());
// Query
let compute = collection.get("compute").unwrap();
assert!(collection.contains("compute"));
// Aggregates
let total_revenue = collection.total_monthly_revenue();
let total_slices = collection.total_slices();
let total_power = collection.total_power_consumption();
let total_cost = collection.total_hardware_cost();
// Serialization
let toml = collection.to_toml().unwrap();
let parsed = NodeCollection::from_toml(&toml).unwrap();
The crate includes full Rhai scripting support with a fluent builder pattern for clean, readable node configuration.
use rhai::Engine;
use sal_capacitymodel::register_capacitymodel_module;
let mut engine = Engine::new();
register_capacitymodel_module(&mut engine).unwrap();
The builder pattern provides a clean, fluent API for creating nodes:
let node = nodespec_builder()
.name("my_server")
.hardware()
.cost(5000.0)
.power(200.0)
.rack_units(2.0)
.done()
.capacity()
.memory(128)
.vcpu(32)
.ssd(2000)
.gpu(2)
.done()
.agents()
.max(16)
.agent_type("enterprise")
.done()
.pricing()
.slice_monthly(50.0)
.agent_monthly(20.0)
.done()
.auto_slices()
.build();
NodeSpecBuilder
nodespec_builder() - Create new builder.name(str) - Set node name.hardware() - Enter hardware configuration.capacity() - Enter capacity configuration.agents() - Enter agent configuration.pricing() - Enter pricing configuration.slices() - Enter slice configuration.auto_slices() - Auto-calculate slice count.build() - Build and return NodeSpecHardwareBuilder (via .hardware())
.cost(usd) - Hardware cost in USD.power(watts) - Power consumption.rack_units(units) - Rack units.done() - Return to NodeSpecBuilderCapacityBuilder (via .capacity())
.memory(gb) - RAM in GB.vcpu(count) - Virtual CPUs.ssd(gb) - SSD storage in GB.hdd(gb) - HDD storage in GB.gpu(count) - Number of GPUs.passmark(score) - CPU benchmark score.ai_performance(tps) - AI tokens/sec benchmark.done() - Return to NodeSpecBuilderAgentBuilder (via .agents())
.max(count) - Maximum agents.agent_type(str) - Agent type name.done() - Return to NodeSpecBuilderPricingBuilder (via .pricing())
.slice_monthly(usd) - Price per slice per month.agent_monthly(usd) - Price per agent per month.hdd_monthly(usd_per_gb) - HDD price per GB per month.done() - Return to NodeSpecBuilderSliceBuilder (via .slices())
.count(n) - Manual slice count.done() - Return to NodeSpecBuilder// Build a compute node
let compute = nodespec_builder()
.name("compute_node")
.hardware()
.cost(2000.0)
.power(150.0)
.done()
.capacity()
.memory(64)
.vcpu(16)
.ssd(500)
.done()
.pricing()
.slice_monthly(10.0)
.done()
.auto_slices()
.build();
print("Node: " + compute.name);
print("Slices: " + compute.nr_slices); // 64/4 = 16
print("Revenue: $" + total_monthly_revenue(compute));
let gpu_server = nodespec_builder()
.name("AI Inference Server")
.hardware()
.cost(50000.0)
.power(2000.0)
.rack_units(4.0)
.done()
.capacity()
.memory(512)
.vcpu(128)
.ssd(4000)
.gpu(4)
.passmark(200000)
.ai_performance(400000)
.done()
.agents()
.max(64)
.agent_type("inference")
.done()
.pricing()
.slice_monthly(500.0)
.agent_monthly(100.0)
.done()
.auto_slices() // Will set 4 slices (1 per GPU)
.build();
let slice = slice_capacity(gpu_server);
print("Per slice: " + slice.memory_gb + " GB, " + slice.gpu + " GPU");
let dc = node_collection_new();
// Add compute nodes
let i = 0;
while i < 10 {
let node = nodespec_builder()
.name("compute_" + i)
.hardware().cost(3000.0).power(200.0).done()
.capacity().memory(128).vcpu(32).ssd(1000).done()
.pricing().slice_monthly(12.0).done()
.auto_slices()
.build();
add(dc, node);
i += 1;
}
// Add GPU servers
i = 0;
while i < 3 {
let node = nodespec_builder()
.name("gpu_" + i)
.hardware().cost(50000.0).power(2000.0).done()
.capacity().memory(512).vcpu(128).gpu(4).done()
.pricing().slice_monthly(500.0).done()
.auto_slices()
.build();
add(dc, node);
i += 1;
}
// Datacenter stats
print("Total nodes: " + len(dc));
print("Total slices: " + total_slices(dc));
print("Monthly revenue: $" + total_monthly_revenue(dc));
print("Hardware cost: $" + total_hardware_cost(dc));
These functions are still available for compatibility:
nodespec_new() - Create empty NodeSpecnodespec_from_toml(toml_str) - Parse from TOMLnodespec_from_heroscript(script) - Parse from HeroScript (returns array)nodespec_preset_compute() - Compute node presetnodespec_preset_workstation() - Workstation presetnodespec_preset_server_quatro() - Quad-GPU server presetto_toml(node) - Export to TOMLto_heroscript(node) - Export to HeroScriptcalculate_nr_slices(node) - Calculate optimal slicesauto_configure_slices(node) - Auto-set nr_slicesslices_fit(node) - Check if slices fit capacityslice_capacity(node) - Get per-slice capacitytotal_monthly_revenue(node) - Get monthly revenueslice_price_per_hour(node) - On-demand hourly priceslice_price_per_hour_committed(node) - Committed hourly priceagent_price_per_hour(node) - Agent on-demand priceagent_price_per_hour_committed(node) - Agent committed pricenormalize(node) - Normalize identifiersvalidate(node) - Validate configurationsummary(node) - Human-readable summaryname, nr_slices, memory_gb, vcpu, ssd_gb, hdd_gb, nr_gpumax_agents, agent_type, hardware_cost_usd, power_consumption_wattsnode_price_per_slice_per_month, agent_price_per_monthmemory_gb, vcpu, ssd_gb, hdd_gb, gpunode_collection_new() - Create empty collectionnode_collection_from_toml(toml_str) - Parse from TOMLadd(collection, node) - Add nodeget(collection, name) - Get node by nameremove(collection, name) - Remove nodecontains(collection, name) - Check if existslen(collection) - Get countis_empty(collection) - Check if emptynames(collection) - Get all names (array)to_toml(collection) - Export to TOMLtotal_monthly_revenue(collection) - Sum of revenuestotal_hardware_cost(collection) - Sum of hardware coststotal_power_consumption(collection) - Sum of powertotal_slices(collection) - Sum of slicessummary(collection) - Human-readable summarylet heroscript = `
!!nodespec.define
name: 'api_server'
memory_gb: 128
vcpu: 32
ssd_gb: 2000
nr_slices: 32
node_price_per_slice_per_month: 15.0
`;
let nodes = nodespec_from_heroscript(heroscript);
for node in nodes {
print("Loaded: " + node.name);
print(summary(node));
}
See examples/rhai/ for complete examples:
01_basic_nodespec.rhai - Basic NodeSpec creation and usage02_custom_nodespec.rhai - Creating custom configurations03_node_collection.rhai - Working with NodeCollection04_datacenter_planning.rhai - Realistic datacenter planning05_heroscript_integration.rhai - HeroScript parsing and export06_pricing_comparison.rhai - Pricing analysis and comparisoncargo test -p sal-capacitymodel