use super::physical::NixlMetadata;
use super::{BlockDimension, KvBlockLayout, LayoutConfig};
use anyhow::Result;
use dynamo_memory::{MemoryRegion, StorageKind};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum BlockFormat {
#[default]
Operational,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullyContiguousDetails {
pub block_format: BlockFormat,
#[serde(default)]
pub kv_block_layout: KvBlockLayout,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayerSeparateDetails {
pub block_dim: BlockDimension,
#[serde(default)]
pub kv_block_layout: KvBlockLayout,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LayoutTypeDetails {
FullyContiguous(FullyContiguousDetails),
LayerSeparate(LayerSeparateDetails),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutDescriptor {
pub version: u32,
pub layout_config: LayoutConfig,
pub location: StorageKind,
pub nixl_metadata: NixlMetadata,
pub memory_descriptors: Vec<MemoryRegion>,
pub layout_type_details: LayoutTypeDetails,
}
impl LayoutDescriptor {
pub const CURRENT_VERSION: u32 = 1;
pub fn to_json(&self) -> Result<String> {
serde_json::to_string(self)
.map_err(|e| anyhow::anyhow!("failed to serialize layout to JSON: {}", e))
}
pub fn to_json_bytes(&self) -> Result<Vec<u8>> {
serde_json::to_vec(self)
.map_err(|e| anyhow::anyhow!("failed to serialize layout to JSON bytes: {}", e))
}
pub fn from_json(json: &str) -> Result<Self> {
serde_json::from_str(json)
.map_err(|e| anyhow::anyhow!("failed to deserialize layout from JSON: {}", e))
}
pub fn from_json_bytes(bytes: &[u8]) -> Result<Self> {
serde_json::from_slice(bytes)
.map_err(|e| anyhow::anyhow!("failed to deserialize layout from JSON bytes: {}", e))
}
pub fn layout_config(&self) -> &LayoutConfig {
&self.layout_config
}
pub fn location(&self) -> StorageKind {
self.location
}
pub fn nixl_metadata(&self) -> &NixlMetadata {
&self.nixl_metadata
}
pub fn memory_descriptors(&self) -> &[MemoryRegion] {
&self.memory_descriptors
}
pub fn layout_type_details(&self) -> &LayoutTypeDetails {
&self.layout_type_details
}
}
#[cfg(all(test, feature = "testing-kvbm"))]
mod tests {
use dynamo_memory::nixl::MemType;
use super::*;
fn make_test_config() -> LayoutConfig {
LayoutConfig::builder()
.num_blocks(10)
.num_layers(4)
.outer_dim(2)
.page_size(16)
.inner_dim(128)
.dtype_width_bytes(2)
.build()
.unwrap()
}
#[test]
fn test_block_format_default() {
assert_eq!(BlockFormat::default(), BlockFormat::Operational);
}
#[test]
fn test_serialized_layout_json_roundtrip() {
let layout = LayoutDescriptor {
version: LayoutDescriptor::CURRENT_VERSION,
layout_config: make_test_config(),
location: StorageKind::System,
nixl_metadata: NixlMetadata::new("test_agent".to_string(), MemType::Dram, 0),
memory_descriptors: vec![MemoryRegion::new(0x1000, 4096)],
layout_type_details: LayoutTypeDetails::FullyContiguous(FullyContiguousDetails {
block_format: BlockFormat::Operational,
kv_block_layout: KvBlockLayout::OperationalNHD,
}),
};
let json = layout.to_json().unwrap();
let deserialized = LayoutDescriptor::from_json(&json).unwrap();
assert_eq!(deserialized.version, layout.version);
assert_eq!(deserialized.layout_config, layout.layout_config);
assert_eq!(deserialized.location, layout.location);
assert_eq!(
deserialized.nixl_metadata.agent_name(),
layout.nixl_metadata.agent_name()
);
assert_eq!(deserialized.memory_descriptors.len(), 1);
}
#[test]
fn test_serialized_layout_json_bytes_roundtrip() {
let layout = LayoutDescriptor {
version: LayoutDescriptor::CURRENT_VERSION,
layout_config: make_test_config(),
location: StorageKind::System,
nixl_metadata: NixlMetadata::new("test_agent".to_string(), MemType::Vram, 5),
memory_descriptors: vec![
MemoryRegion::new(0x1000, 2048),
MemoryRegion::new(0x2000, 2048),
],
layout_type_details: LayoutTypeDetails::LayerSeparate(LayerSeparateDetails {
block_dim: BlockDimension::BlockIsFirstDim,
kv_block_layout: KvBlockLayout::OperationalNHD,
}),
};
let bytes = layout.to_json_bytes().unwrap();
let deserialized = LayoutDescriptor::from_json_bytes(&bytes).unwrap();
assert_eq!(deserialized.version, layout.version);
assert_eq!(deserialized.nixl_metadata.device_id(), 5);
assert_eq!(deserialized.memory_descriptors.len(), 2);
}
#[test]
fn test_fully_contiguous_details_serialization() {
let details = LayoutTypeDetails::FullyContiguous(FullyContiguousDetails {
block_format: BlockFormat::Operational,
kv_block_layout: KvBlockLayout::UniversalTP,
});
let json = serde_json::to_string(&details).unwrap();
let deserialized: LayoutTypeDetails = serde_json::from_str(&json).unwrap();
match deserialized {
LayoutTypeDetails::FullyContiguous(d) => {
assert_eq!(d.block_format, BlockFormat::Operational);
assert_eq!(d.kv_block_layout, KvBlockLayout::UniversalTP);
}
_ => panic!("Expected FullyContiguous variant"),
}
}
#[test]
fn test_layer_separate_details_serialization() {
let details = LayoutTypeDetails::LayerSeparate(LayerSeparateDetails {
block_dim: BlockDimension::BlockIsSecondDim,
kv_block_layout: KvBlockLayout::OperationalHND,
});
let json = serde_json::to_string(&details).unwrap();
let deserialized: LayoutTypeDetails = serde_json::from_str(&json).unwrap();
match deserialized {
LayoutTypeDetails::LayerSeparate(d) => {
assert_eq!(d.block_dim, BlockDimension::BlockIsSecondDim);
assert_eq!(d.kv_block_layout, KvBlockLayout::OperationalHND);
}
_ => panic!("Expected LayerSeparate variant"),
}
}
}