Skip to content

feat: add ai models to sbom details page#968

Open
carlosthe19916 wants to merge 3 commits intoguacsec:mainfrom
carlosthe19916:feat/ai-models
Open

feat: add ai models to sbom details page#968
carlosthe19916 wants to merge 3 commits intoguacsec:mainfrom
carlosthe19916:feat/ai-models

Conversation

@carlosthe19916
Copy link
Collaborator

@carlosthe19916 carlosthe19916 commented Mar 18, 2026

Counterpart of guacsec/trustify#2255

Screencast.From.2026-03-18.18-46-30.mp4

Summary by Sourcery

Add backend API contract and frontend UI to list and inspect AI models associated with an SBOM on the SBOM details page.

New Features:

  • Introduce /api/v2/sbom/{id}/models endpoint and associated PaginatedResults_SbomModel and SbomModel schemas for querying AI models linked to an SBOM.
  • Add React query hook and SBOM details page tab to display paginated, filterable AI models for a given SBOM, including a drawer with detailed model metadata and external references.

Enhancements:

  • Add a reusable ConditionalDataListBody helper component to standardize loading, error, and empty states for data lists.

Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 18, 2026

Reviewer's Guide

Adds AI model support to the SBOM details page by introducing a new backend listModels endpoint, React query hook, models tab with list view and detail drawer, and shared conditional DataList wrapper for loading/error/empty states.

Sequence diagram for fetching AI models on the SBOM details page

sequenceDiagram
  actor User
  participant SbomDetails
  participant ModelsBySbom
  participant useFetchModelsBySbomId
  participant ReactQuery
  participant trustdClient
  participant TrustdAPI

  User->>SbomDetails: Open SBOM details page
  SbomDetails->>SbomDetails: Initialize tabs info, packages, vulnerabilities, models
  User->>SbomDetails: Select models tab
  SbomDetails->>ModelsBySbom: Render ModelsBySbom(sbomId)

  ModelsBySbom->>useFetchModelsBySbomId: Call hook with sbomId, table params
  useFetchModelsBySbomId->>ReactQuery: useQuery(listModels, queryKey)
  ReactQuery->>trustdClient: listModels(path id, query params)
  trustdClient->>TrustdAPI: GET /api/v2/sbom/{id}/models
  TrustdAPI-->>trustdClient: 200 PaginatedResults_SbomModel
  trustdClient-->>ReactQuery: AxiosResponse(data)
  ReactQuery-->>useFetchModelsBySbomId: data, isLoading, error
  useFetchModelsBySbomId-->>ModelsBySbom: result (items, total), isFetching

  ModelsBySbom->>ConditionalDataListBody: Render with isLoading, isError, isNoData
  alt models returned
    ModelsBySbom->>ModelsBySbom: Render DataList of models
    User->>ModelsBySbom: Click model name
    ModelsBySbom->>ModelDetailDrawer: Open drawer with SbomModel
  else no models
    ConditionalDataListBody-->>User: Show no models empty state
  end
Loading

Entity-relationship diagram for SBOM and AI models API types

erDiagram
  Sbom {
    uuid id
  }

  SbomModel {
    string id
    string name
    string purl
    object properties
  }

  PaginatedResults_SbomModel {
    SbomModel[] items
    int total
  }

  Sbom ||--o{ SbomModel : has
  PaginatedResults_SbomModel }o--o{ SbomModel : wraps
Loading

Updated class diagram for SBOM AI models frontend and API types

classDiagram
  class SbomModel {
    +string id
    +string name
    +any properties
    +any purl
  }

  class PaginatedResults_SbomModel {
    +SbomModel[] items
    +int total
  }

  class ModelProperties {
    +string version
    +string licenses
    +string bomFormat
    +string suppliedBy
    +string specVersion
    +string typeOfModel
    +string serialNumber
    +string primaryPurpose
    +string downloadLocation
    +string external_references
    +string limitation
    +string safetyRiskAssessment
  }

  class ExternalReference {
    +string type
    +string url
    +string comment
  }

  class ModelDetailDrawerProps {
    +SbomModel model
  }

  class ModelDetailDrawer {
    +render(props ModelDetailDrawerProps) ReactElement
  }

  class ModelsProps {
    +string sbomId
  }

  class ModelsBySbom {
    +SbomModel selectedModel
    +render(props ModelsProps) ReactElement
  }

  class IConditionalDataListBodyProps {
    +bool isLoading
    +bool isError
    +bool isNoData
    +ReactNode errorEmptyState
    +ReactNode noDataEmptyState
    +ReactNode children
  }

  class ConditionalDataListBody {
    +render(props IConditionalDataListBodyProps) ReactElement
  }

  class useFetchModelsBySbomIdHook {
    +execute(sbomId string, params HubRequestParams) UseFetchModelsBySbomIdResult
  }

  class UseFetchModelsBySbomIdResult {
    +UseFetchModelsBySbomIdResultData result
    +bool isFetching
    +AxiosError fetchError
    +function refetch
  }

  class UseFetchModelsBySbomIdResultData {
    +SbomModel[] data
    +int total
    +HubRequestParams params
  }

  class listModelsFunction {
    +call(client any, id string, query any) Promise~PaginatedResults_SbomModel~
  }

  class HubRequestParams
  class AxiosError
  class ReactElement
  class ReactNode

  SbomModel --> ModelProperties : properties as
  ModelDetailDrawer --> SbomModel : uses
  ModelDetailDrawer --> ModelProperties : getModelProperties
  ModelDetailDrawer --> ExternalReference : parseExternalReferences

  ModelsBySbom --> SbomModel : lists
  ModelsBySbom --> ModelDetailDrawer : opens drawer
  ModelsBySbom --> ConditionalDataListBody : wraps list
  ModelsBySbom --> useFetchModelsBySbomIdHook : calls

  useFetchModelsBySbomIdHook --> listModelsFunction : uses
  useFetchModelsBySbomIdHook --> PaginatedResults_SbomModel : returns data

  PaginatedResults_SbomModel --> SbomModel : contains
  ConditionalDataListBody --> IConditionalDataListBodyProps : uses props

  useFetchModelsBySbomIdHook --> UseFetchModelsBySbomIdResult : returns
  UseFetchModelsBySbomIdResult --> UseFetchModelsBySbomIdResultData : has

  listModelsFunction --> PaginatedResults_SbomModel : resolves to
Loading

File-Level Changes

Change Details Files
Extend OpenAPI schema to expose an SBOM-scoped AI models listing endpoint and model types.
  • Add /api/v2/sbom/{id}/models GET endpoint with filtering, sorting, and pagination query parameters.
  • Introduce PaginatedResults_SbomModel schema mirroring existing paginated results patterns for SBOM entities.
  • Define SbomModel component schema describing model identity, PURL, and arbitrary properties.
client/openapi/trustd.yaml
Add a React Query hook to fetch AI models for a given SBOM using the new API.
  • Wire listModels client function into sboms query module imports.
  • Implement useFetchModelsBySbomId hook that builds Hub-style request params, calls listModels, and normalizes the result shape to items/total/params with loading and error state.
client/src/app/queries/sboms.ts
Extend SBOM details page UI with a new Models tab wired to a models listing component and drawer.
  • Add a models tab key and corresponding tab refs to the tab control configuration with URL persistence.
  • Render new Tab and TabContent for models, hooking aria labels and conditional rendering based on sbomId.
  • Mount ModelsBySbom component within the models tab to display AI models for the selected SBOM.
client/src/app/pages/sbom-details/sbom-details.tsx
Implement the models list view with filtering, pagination, and a side drawer showing detailed model metadata.
  • Create ModelsBySbom component that uses table control hooks and useFetchModelsBySbomId to fetch paginated, filterable model data per SBOM.
  • Render PatternFly DataList rows with model name (clickable to open drawer), PURL, supplier, and license information.
  • Integrate PageDrawerContent that shows ModelDetailDrawer for the selected model with open/close behavior.
client/src/app/pages/sbom-details/models-by-sbom.tsx
Add a detail drawer component to display structured AI model metadata from SBOM properties, including external references and download links.
  • Define ModelProperties and ExternalReference interfaces and helper functions to coerce arbitrary properties and parse external_references JSON safely.
  • Render multiple PatternFly cards for identity/purpose, SBOM metadata, and external references, including labels and external links.
  • Expose ModelDetailDrawer as a reusable component that takes an SbomModel and derives its presentation from the model.properties payload.
client/src/app/pages/sbom-details/model-detail-drawer.tsx
Introduce a reusable conditional DataList body wrapper for handling loading, error, and empty states.
  • Create ConditionalDataListBody component that switches between spinner, error state, no-data state, or children based on flags.
  • Expose ConditionalDataListBody via a new DataListControls barrel index for reuse in list views like models-by-sbom.
client/src/app/components/DataListControls/ConditionalDataListBody.tsx
client/src/app/components/DataListControls/index.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In the OpenAPI schema, PaginatedResults_SbomModel.items defines an inline object instead of reusing the SbomModel component, which duplicates the shape and risks divergence; consider referencing #/components/schemas/SbomModel there instead.
  • The properties and purl fields in both PaginatedResults_SbomModel.items and SbomModel lack explicit type declarations, which can cause issues for code generators and validators; declare their types (e.g., type: string or a more specific schema) consistently.
  • In useFetchModelsBySbomId, the params: params ?? params expression in the returned result object is redundant and can be simplified to just params.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the OpenAPI schema, `PaginatedResults_SbomModel.items` defines an inline object instead of reusing the `SbomModel` component, which duplicates the shape and risks divergence; consider referencing `#/components/schemas/SbomModel` there instead.
- The `properties` and `purl` fields in both `PaginatedResults_SbomModel.items` and `SbomModel` lack explicit `type` declarations, which can cause issues for code generators and validators; declare their types (e.g., `type: string` or a more specific schema) consistently.
- In `useFetchModelsBySbomId`, the `params: params ?? params` expression in the returned `result` object is redundant and can be simplified to just `params`.

## Individual Comments

### Comment 1
<location path="client/openapi/trustd.yaml" line_range="4994-5003" />
<code_context>
           type: integer
           format: int64
           minimum: 0
+    PaginatedResults_SbomModel:
+      type: object
+      required:
+      - items
+      - total
+      properties:
+        items:
+          type: array
+          items:
+            type: object
+            required:
+            - id
+            - name
+            - purl
+            - properties
+            properties:
+              id:
+                type: string
+                description: The internal ID of a model
+              name:
+                type: string
+                description: The name of the model in the SBOM
+              properties:
+                description: The properties associated with the model
+              purl:
+                description: The model's PURL
+        total:
</code_context>
<issue_to_address>
**issue (bug_risk):** Add explicit types for `properties` and `purl` (and consider referencing `SbomModel`) to improve schema correctness and client generation.

In `PaginatedResults_SbomModel.items.properties` (and in `SbomModel`), `properties` and `purl` only have descriptions, so client generators may infer them incorrectly or as `any`. Please explicitly set their types (e.g., `type: object` for `properties`, `type: string` for `purl`), and consider replacing the inline `type: object` under `items` with a `$ref: '#/components/schemas/SbomModel'` to keep the model definition centralized and consistent.
</issue_to_address>

### Comment 2
<location path="client/openapi/trustd.yaml" line_range="5686-5703" />
<code_context>
             type: string
           v3Signatures:
             type: boolean
+    SbomModel:
+      type: object
+      required:
+      - id
+      - name
+      - purl
+      - properties
+      properties:
+        id:
+          type: string
+          description: The internal ID of a model
+        name:
+          type: string
+          description: The name of the model in the SBOM
+        properties:
+          description: The properties associated with the model
+        purl:
+          description: The model's PURL
     SbomPackage:
</code_context>
<issue_to_address>
**suggestion:** Clarify the schema for `properties` and `purl` on `SbomModel` to avoid ambiguous or weakly-typed clients.

`SbomModel.properties` and `SbomModel.purl` currently lack explicit `type` definitions. Since the UI expects `properties` to be an object with known keys and `purl` to be a string, please add explicit types (e.g. `properties: { type: object, additionalProperties: true }` or a more detailed object schema, and `purl: { type: string }`) so generated client types are accurate and integration issues are caught earlier.

```suggestion
    SbomModel:
      type: object
      required:
      - id
      - name
      - purl
      - properties
      properties:
        id:
          type: string
          description: The internal ID of a model
        name:
          type: string
          description: The name of the model in the SBOM
        properties:
          type: object
          description: The properties associated with the model
          additionalProperties: true
        purl:
          type: string
          description: The model's PURL
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +4994 to +5003
PaginatedResults_SbomModel:
type: object
required:
- items
- total
properties:
items:
type: array
items:
type: object
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Add explicit types for properties and purl (and consider referencing SbomModel) to improve schema correctness and client generation.

In PaginatedResults_SbomModel.items.properties (and in SbomModel), properties and purl only have descriptions, so client generators may infer them incorrectly or as any. Please explicitly set their types (e.g., type: object for properties, type: string for purl), and consider replacing the inline type: object under items with a $ref: '#/components/schemas/SbomModel' to keep the model definition centralized and consistent.

Comment on lines +5686 to +5703
SbomModel:
type: object
required:
- id
- name
- purl
- properties
properties:
id:
type: string
description: The internal ID of a model
name:
type: string
description: The name of the model in the SBOM
properties:
description: The properties associated with the model
purl:
description: The model's PURL
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Clarify the schema for properties and purl on SbomModel to avoid ambiguous or weakly-typed clients.

SbomModel.properties and SbomModel.purl currently lack explicit type definitions. Since the UI expects properties to be an object with known keys and purl to be a string, please add explicit types (e.g. properties: { type: object, additionalProperties: true } or a more detailed object schema, and purl: { type: string }) so generated client types are accurate and integration issues are caught earlier.

Suggested change
SbomModel:
type: object
required:
- id
- name
- purl
- properties
properties:
id:
type: string
description: The internal ID of a model
name:
type: string
description: The name of the model in the SBOM
properties:
description: The properties associated with the model
purl:
description: The model's PURL
SbomModel:
type: object
required:
- id
- name
- purl
- properties
properties:
id:
type: string
description: The internal ID of a model
name:
type: string
description: The name of the model in the SBOM
properties:
type: object
description: The properties associated with the model
additionalProperties: true
purl:
type: string
description: The model's PURL

Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
@codecov
Copy link

codecov bot commented Mar 19, 2026

Codecov Report

❌ Patch coverage is 39.62264% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.70%. Comparing base (73832b5) to head (ebacc06).

Files with missing lines Patch % Lines
...src/app/pages/sbom-details/model-detail-drawer.tsx 12.50% 21 Missing ⚠️
...ient/src/app/pages/sbom-details/models-by-sbom.tsx 50.00% 6 Missing and 1 partial ⚠️
...nents/DataListControls/ConditionalDataListBody.tsx 50.00% 2 Missing and 1 partial ⚠️
client/src/app/queries/sboms.ts 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #968      +/-   ##
==========================================
- Coverage   67.24%   66.70%   -0.54%     
==========================================
  Files         218      221       +3     
  Lines        3828     3881      +53     
  Branches      873      898      +25     
==========================================
+ Hits         2574     2589      +15     
- Misses        915      955      +40     
+ Partials      339      337       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant