Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/ui/src/entities/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Requests and responses for entity-list queries.
pub mod request;
pub mod response;
137 changes: 137 additions & 0 deletions crates/ui/src/entities/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashSet;

use quent_analyzer::{AnalyzerResult, Model, resource::tree::ResourceTreeNode};
use quent_time::{TimeError, TimeSec, TimeUnixNanoSec, span::SpanUnixNanoSec, to_nanosecs};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;

use crate::paginate::PageParams;

/// Restricts returned entities to appear in a certain scope.
#[derive(TS, Debug, Clone, Serialize, Deserialize)]
pub enum EntityScope {
/// Only return entities that use this resource.
Resource { resource_id: Uuid },
/// Only return entities that use any resource within this group.
ResourceGroup {
resource_group_id: Uuid,
resource_type_name: String,
},
}

impl EntityScope {
/// Resolve the scope to the resource IDs it covers: the single resource, or
/// the leaf resources of the group that have the requested type.
pub fn resolve(&self, model: &impl Model) -> AnalyzerResult<HashSet<Uuid>> {
match self {
EntityScope::Resource { resource_id } => {
model.resource(*resource_id)?;
Ok([*resource_id].into_iter().collect())
}
EntityScope::ResourceGroup {
resource_group_id,
resource_type_name,
} => {
let tree = ResourceTreeNode::try_new(model, *resource_group_id)?;
Ok(tree
.iter_leaf_ids()

@dhruv9vats dhruv9vats Jul 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean that only leaf resources should have valid usages ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this limitation is removed as a part of #191.

The resource concept will be unrelated to hierarchical trees of entities, also see:

https://github.com/rapidsai/quent/blob/main/crates/ref-tree/src/lib.rs
https://github.com/rapidsai/quent/blob/main/crates/resource/src/lib.rs

.filter(|&id| {
model
.resource(id)
.is_ok_and(|r| r.type_name() == resource_type_name)
})
.collect())
}
}
}
}

/// A time window, resolved against an epoch supplied at conversion.
#[derive(TS, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct TimeWindow {
/// The start time of the window in seconds.
pub start: TimeSec,
/// The end time of the window in seconds.
pub end: TimeSec,
}

impl TimeWindow {
/// Resolve to an absolute span by offsetting from `epoch`.
pub fn try_into_span(self, epoch: TimeUnixNanoSec) -> Result<SpanUnixNanoSec, TimeError> {
SpanUnixNanoSec::try_new(
epoch + to_nanosecs(self.start),
epoch + to_nanosecs(self.end),
)
}
}

/// Entity filters.
///
/// Every field that is set must match, `None` fields do not filter.
#[derive(TS, Debug, Clone, Default, Serialize, Deserialize)]
pub struct EntityListFilter {
/// Restrict resulting entities to be in this scope.
pub scope: Option<EntityScope>,
/// Restrict resulting entities to be of this type.
pub entity_type_name: Option<String>,
/// Keep only entities with resource usages longer than this threshold.
///
/// N.B. only Fsm-type entities can have usages.
pub min_usage_s: Option<TimeSec>,
}

/// The key entities are sorted by.
#[derive(TS, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum EntitySortKey {
/// The longest single resource-usage span within the window.
UsageDuration,
}

/// The direction to sort in.
#[derive(TS, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum SortDir {
/// Sort in ascending order.
Asc,
/// Sort in descending order.
Desc,
}

/// Sorting parameters.
#[derive(TS, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Sort {
/// The key to sort on.
pub key: EntitySortKey,
/// The direction to sort in.
pub dir: SortDir,
}

/// A single entity-list query.
#[derive(TS, Debug, Clone, Serialize, Deserialize)]
pub struct EntityListEntry<EntryParams> {
/// The window of time entities must fall in.
pub window: TimeWindow,
/// Filter parameters.
pub filter: EntityListFilter,
/// Sort parameters.
pub sort: Sort,
/// Pagination parameters.
///
/// When this is not set, return the full list. Depending on the dataset
/// size and other parameters, this may result in a large volume of data and
/// should be used with care.
pub page: Option<PageParams>,
/// Per-query application-specific parameters.
pub application: EntryParams,
}

/// Parameters for listing entities.
#[derive(TS, Debug, Clone, Serialize, Deserialize)]
pub struct EntityListRequest<GlobalParams, EntryParams> {
pub entry: EntityListEntry<EntryParams>,
/// Global application parameters shared by the query.
pub app_params: GlobalParams,
}
17 changes: 17 additions & 0 deletions crates/ui/src/entities/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use serde::Serialize;
use ts_rs::TS;

use crate::FiniteStateMachine;

/// A ranked, paged list of entities.
#[derive(TS, Debug, Clone, Serialize)]
pub struct EntityListResponse {
// TODO(johanpel): generalize to other entity types, but only FSMs are
// represented today.
pub items: Vec<FiniteStateMachine>,
/// The count of entities matching the filter before paging.
pub total: u32,
}
54 changes: 54 additions & 0 deletions crates/ui/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;

use quent_analyzer::{self as a, AnalyzerResult, Entity, Model, resource::tree::ResourceTreeNode};
use quent_time::{TimeSec, TimeUnixNanoSec, try_to_secs_relative};
use serde::Serialize;
use ts_rs::TS;
use uuid::Uuid;

pub mod entities;
pub mod paginate;
pub mod quantity;
pub mod timeline;

Expand Down Expand Up @@ -249,4 +253,54 @@ impl FiniteStateMachine {
.collect::<Result<Vec<_>, _>>()?,
})
}

/// Build from any application FSM via the [`FsmUsages`](a::fsm::FsmUsages)
/// interface.
///
/// Usages are grouped onto their state's transition by state name, which is
/// unique within an FSM.
pub fn try_from_fsm<'a, F>(
fsm: &'a F,
epoch: TimeUnixNanoSec,
) -> Result<Self, quent_time::TimeError>
where
F: a::fsm::FsmUsages<'a>,
{
use a::fsm::Transition;
use a::resource::Usage;
use quent_time::Timestamp;

let mut usages_by_state: HashMap<String, Vec<FsmUsage>> = HashMap::new();
for (state_name, usage) in fsm.usages_with_state_names() {
usages_by_state
.entry(state_name.to_owned())
.or_default()
.push(FsmUsage {
resource: usage.resource_id(),
capacities: usage
.capacities()
.map(|c| (c.name.to_string(), c.value))
.collect(),
});
}

// 0..=len covers every transition including the exit transition.
let transitions = (0..=fsm.len())
.filter_map(|i| fsm.transition(i))
.map(|t| {
Ok(FsmTransition {
name: t.name().to_owned(),
usages: usages_by_state.remove(t.name()).unwrap_or_default(),
timestamp: try_to_secs_relative(t.timestamp(), epoch)?,
})
})
.collect::<Result<Vec<_>, quent_time::TimeError>>()?;

Ok(Self {
id: fsm.id(),
type_name: fsm.type_name().to_owned(),
instance_name: fsm.instance_name().to_owned(),
transitions,
})
}
}
16 changes: 16 additions & 0 deletions crates/ui/src/paginate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

//! Pagination parameters shared across list endpoints.

use serde::{Deserialize, Serialize};
use ts_rs::TS;

/// Parameters for paginated lists.
#[derive(TS, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct PageParams {
/// The maximum size of a page.
pub max: u32,
/// The zero-based index of the requested page.
pub page: u32,
}
Loading
Loading