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
13 changes: 13 additions & 0 deletions workspaces/boost/.changeset/ai-catalog-frontend-polish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@red-hat-developer-hub/backstage-plugin-boost': minor
Comment thread
rohitkrai03 marked this conversation as resolved.
---

Polish responsive AI Catalog browsing and AI asset entity details with dynamic
filters, accessible view switching, usage guidance, and type-aware metadata.

Entity-page extensions are consolidated into AI asset details, agent
instructions, and Usage cards. The separate Usage tab is not part of this
preview; standard TechDocs remain provided by the host application, and usage
content remains hidden when the usage permission is denied. Git sources that
point to subpaths now open their source location instead of guessing an
archive download.
7 changes: 7 additions & 0 deletions workspaces/boost/.changeset/ogx-asset-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@red-hat-developer-hub/backstage-plugin-ogx-entity-provider': minor
'@red-hat-developer-hub/backstage-plugin-boost-common': patch
---

Align OGX AI asset entities with the canonical provider metadata contract and
harden shared AI asset type validation.
18 changes: 17 additions & 1 deletion workspaces/boost/app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ catalog:
- Component
- System
- Group
- User
- Resource
- Location
- Template
Expand All @@ -61,7 +62,16 @@ catalog:
- type: file
target: ../../fixtures/ai-catalog-fixtures.yaml
rules:
- allow: [AiResource, AiModelServerAPI, API, Component, Resource, Group]
- allow:
[
AiResource,
AiModelServerAPI,
API,
Component,
Resource,
Group,
User,
]

boost:
security:
Expand Down Expand Up @@ -111,6 +121,7 @@ boost:
- procurement
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin
- id: legal
name: Legal
description: Handles questions about software licenses, embargoes, privacy/PII, contracts, policies, procedures, or compliance
Expand All @@ -123,6 +134,7 @@ boost:
enableRAG: true
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin
- id: support
name: Software Support
description: Handles technical support questions about OpenShift/Kubernetes, deployment, permissions, performance, and FantaCo products
Expand All @@ -135,6 +147,7 @@ boost:
enableRAG: true
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin
- id: hr
name: Human Resources
description: Handles questions about employee benefits, health care, vacation/PTO, retirement, workspaces, and compensation
Expand All @@ -157,6 +170,7 @@ boost:
enableRAG: true
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin
- id: sales
name: Sales
description: Handles questions about sales territories, leads, discounting, quotas, CRM, and performance metrics
Expand All @@ -183,6 +197,7 @@ boost:
enableRAG: true
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin
- id: procurement
name: Procurement
description: Handles questions about competitive bidding, vendor evaluation, procurement ethics, and approval processes
Expand All @@ -207,6 +222,7 @@ boost:
enableRAG: true
model: ${BOOST_MODEL:-}
lifecycleStage: published
createdBy: user:default/admin

providers:
ogx:
Expand Down
56 changes: 52 additions & 4 deletions workspaces/boost/e2e-tests/boost.AiCatalogPage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,8 @@ test.describe('Boost AI Catalog', () => {
}) => {
await loadTwoAssetCatalog(page);

const filters = page.getByRole('navigation', { name: 'AI Catalog' });
await filters
.getByRole('button', { name: 'Select an option Type' })
.click();
const filters = page.getByRole('navigation', { name: 'Filters' });
await filters.getByRole('button', { name: 'All Type' }).click();

const typeListbox = page.getByRole('listbox', { name: 'Type' });
await expect(typeListbox).toBeVisible();
Expand All @@ -189,6 +187,15 @@ test.describe('Boost AI Catalog', () => {
await expect(
filters.getByRole('button', { name: 'Skills Type' }),
).toBeVisible();

await page.keyboard.press('Escape');
await filters.getByRole('button', { name: 'Skills Type' }).click();
await page
.getByRole('listbox', { name: 'Type' })
.getByRole('option', { name: 'All', exact: true })
.click();
await expect.poll(() => queryParam(page, 'type')).toBeNull();
await expect(catalogCount(page, 2)).toBeVisible();
});

test('search keeps only matching cards and sets q in the URL', async ({
Expand All @@ -202,6 +209,47 @@ test.describe('Boost AI Catalog', () => {
await expect(catalogCount(page, 1)).toBeVisible();
await expect(skillDetailsLink(page)).toBeVisible();
await expect(agentDetailsLink(page)).toHaveCount(0);

await page.getByRole('button', { name: 'Clear all' }).click();
await expect.poll(() => queryParam(page, 'q')).toBeNull();
await expect(catalogCount(page, 2)).toBeVisible();
});

test('uses a mobile filter drawer on smaller screens', async ({
page,
}, testInfo) => {
await page.setViewportSize({ width: 768, height: 900 });
await loadTwoAssetCatalog(page);

const filterButton = page.getByRole('button', { name: 'Filters' });
await expect(filterButton).toBeVisible();
await filterButton.click();

const dialog = page.getByRole('dialog', { name: 'Filters' });
await expect(dialog).toBeVisible();
await expect(
dialog.getByRole('navigation', { name: 'Filters' }),
).toBeVisible();
await runAccessibilityTests(
page,
testInfo,
'mobile-filter-drawer-accessibility.json',
{ disableRules: ['nested-interactive', 'color-contrast'] },
);

await dialog.getByRole('button', { name: 'All Type' }).click();
await page
.getByRole('listbox', { name: 'Type' })
.getByRole('option', { name: 'Skills', exact: true })
.click();
await page.keyboard.press('Escape');

await expect.poll(() => queryParam(page, 'type')).toBe('skill');
await expect(dialog).toBeVisible();

await dialog.getByRole('button', { name: 'Close' }).click();
await expect(dialog).not.toBeVisible();
await expect(filterButton).toBeFocused();
});

test('table view lists both assets in the data table and sets view=table', async ({
Expand Down
72 changes: 70 additions & 2 deletions workspaces/boost/fixtures/ai-catalog-fixtures.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
---
# AI Catalog sample fixtures — one entity per AI asset category.
# Used by the dev app for local development and testing.
# AI Catalog sample fixtures — representative entities for local development.
# These examples focus on the entity types currently exercised by the dev app.

# ---------------------------------------------------------------------------
# User — referenced by the local OGX agent provider configuration
# ---------------------------------------------------------------------------
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: admin
title: Development Admin
spec:
profile:
displayName: Development Admin
email: admin@example.com
memberOf: []

---
# ---------------------------------------------------------------------------
# Owner groups — referenced by AI asset entities below
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -169,7 +184,10 @@ spec:
type: agent
lifecycle: production
owner: team-ai-platform
model: openai/vllm-inference/gpt-4.1
instructions: An AI agent that helps developers with code generation, debugging, architecture decisions, and documentation.
handoffDescription: Routes specialized questions to the appropriate team.
enableRAG: true
location:
type: git
target: https://github.com/example/developer-assistant-agent
Expand All @@ -192,14 +210,64 @@ metadata:
rhdh.io/ai-asset-category: model-server
rhdh.io/ai-asset-version: '3.0.0'
rhdh.io/ai-asset-source: model-registry
links:
- title: API
url: https://granite.example.com/v1
spec:
type: ai-model-server
lifecycle: production
owner: team-ml-ops
serverType: openai-v1
serverUrl: https://granite.example.com/v1
requiresApiKey: true
models:
discoverable: true
available:
- granite-3-code
- granite-3-instruct
default: granite-3-instruct

---
# ---------------------------------------------------------------------------
# AiModelServerAPI / ai-model-server with a larger model catalog
# ---------------------------------------------------------------------------
apiVersion: backstage.io/v1alpha1
kind: AiModelServerAPI
metadata:
name: multi-model-server
title: Multi-Model Server
description: Model server exposing a larger catalog of available models.
tags:
- model-server
- inference
- multi-model
annotations:
rhdh.io/ai-asset-category: model-server
rhdh.io/ai-asset-version: '3.0.0'
rhdh.io/ai-asset-source: model-registry
links:
- title: API
url: https://models.example.com/v1
spec:
type: ai-model-server
lifecycle: production
owner: team-ml-ops
serverType: openai-v1
serverUrl: https://models.example.com/v1
requiresApiKey: true
models:
discoverable: true
available:
- granite-3-code
- granite-3-instruct
- llama-3.1-8b-instruct
- llama-3.1-70b-instruct
- mistral-7b-instruct
- mixtral-8x7b-instruct
- qwen2.5-7b-instruct
- qwen2.5-coder-7b
- gemma-3-12b-it
- phi-4
- deepseek-r1-distill-qwen-32b
- command-r-plus
default: llama-3.1-8b-instruct
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ de/es/fr/it/ja locale files. This work was split out of `ai-catalog-frontend`.
- Add locale files `de.ts`, `es.ts`, `fr.ts`, `it.ts`, `ja.ts`
- Register lazy imports in `createTranslationResource`

The translation module entry (`./boost-translations-module`) already exists.
The translation module entry (`./translations`) already exists.

## Impact

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Each supported language MUST have a complete translation file following the stan

- **GIVEN** the plugin is deployed as a dynamic plugin in RHDH
- **WHEN** the translation module needs to be auto-discovered
- **THEN** a separate entry point re-exports `boostTranslationsModule` as default (e.g., `./boost-translations-module` in `package.json` exports)
- **THEN** the `./translations` package export exposes `boostTranslationsModule` as the default export
- **AND** RHDH auto-discovers the module without explicit `features` array registration

### Requirement: Complete String Coverage
Expand All @@ -53,7 +53,7 @@ Every user-facing string in the plugin MUST be translated.

- **GIVEN** a user views an AI asset entity page in French
- **WHEN** entity cards from the boost plugin render
- **THEN** Summary, Adoption, Version, and Usage tab titles and actions are all in French
- **THEN** AI asset details, agent instructions, and Usage card titles and actions are all in French

#### Scenario: Error and empty state strings translated

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Tasks: AI Catalog frontend translations (RHIDP-15479)

- [x] 1. Translation module auto-discovery entry (`./boost-translations-module`) already exists
- [x] 1. Translation module auto-discovery entry (`./translations`) already exists
- [ ] 2. Create `src/translations/de.ts`
- [ ] 3. Create `src/translations/es.ts`
- [ ] 4. Create `src/translations/fr.ts`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ The OCI-packaged plugin MUST load correctly in an RHDH deployment.

- **GIVEN** the boost frontend dynamic plugin is installed
- **WHEN** a user navigates to a catalog entity page for an AI asset
- **THEN** the Summary, Adoption, and Version cards render when their required entity data exists
- **AND** the Usage tab (`entity-content:boost/usage`) is present on AI assets
- **THEN** the AI asset details, agent instructions, and Usage cards render when their required entity data exists
- **AND** they use the `entity-card:boost/ai-asset-details`,
`entity-card:boost/agent-instructions`, and `entity-card:boost/usage`
extension IDs

#### Scenario: Extensions absent on non-AI entities

Expand All @@ -60,16 +62,16 @@ Deployers MUST be able to customize the plugin via `app.extensions` in `app-conf

#### Scenario: Disable an entity card

- **GIVEN** the deployer sets `entity-card:boost/adoption: false` in `app.extensions`
- **GIVEN** the deployer sets `entity-card:boost/ai-asset-details: false` in `app.extensions`
- **WHEN** a user views an AI asset entity page
- **THEN** the Download/Adopt Card is not rendered
- **THEN** the AI asset details card is not rendered
- **AND** other boost cards still render

#### Scenario: Change entity filter on a card

- **GIVEN** the deployer sets `entity-card:boost/summary` with a `config.filter` that excludes AI asset kinds
- **GIVEN** the deployer sets `entity-card:boost/ai-asset-details` with a `config.filter` that excludes AI asset kinds
- **WHEN** a user views an AiResource entity page
- **THEN** the summary card is not rendered (filter excludes AiResource)
- **THEN** the AI asset details card is not rendered (filter excludes AiResource)

#### Scenario: Disable the page

Expand Down
Loading
Loading