Skip to content

feat(intelligent-assistant): add a label to make RAG sources distinguishable - #4178

Open
debsmita1 wants to merge 2 commits into
redhat-developer:mainfrom
debsmita1:label-rag-sources
Open

feat(intelligent-assistant): add a label to make RAG sources distinguishable#4178
debsmita1 wants to merge 2 commits into
redhat-developer:mainfrom
debsmita1:label-rag-sources

Conversation

@debsmita1

@debsmita1 debsmita1 commented Aug 5, 2026

Copy link
Copy Markdown
Member

Hey, I just made a Pull Request!

Resolves:

https://redhat.atlassian.net/browse/RHIDP-14152

Solution description:
Show each RAG citation’s LCORE source as a pill label on source cards and in the sources modal so knowledge bases are distinguishable.

Screenshot:
Screenshot 2026-08-05 at 11 25 58 PM

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

@rhdh-gh-app

rhdh-gh-app Bot commented Aug 5, 2026

Copy link
Copy Markdown

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-intelligent-assistant workspaces/intelligent-assistant/plugins/intelligent-assistant patch v3.2.0

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

feat(intelligent-assistant): label RAG sources with LCORE source field

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds a pill-style label showing the LCORE source field on each RAG document card and in the
 sources modal.
• Introduces a reusable RagSourceLabel component and shared style object for consistent label
 appearance.
• Migrates LightspeedChatBox and SourcesChipModal styling from @material-ui/core makeStyles
 to @mui/material styled API.
• Adds tests covering subtitle/label rendering and omission when no source is present.
Diagram

graph TD
  A["LCORE ReferencedDocument.source"] --> B["transformDocumentsToSources()"] --> C["SourcesCardProps.subtitle"]
  C --> D["LightspeedChatBox source card"]
  C --> E["SourcesChipModal"]
  D --> F["RagSourceLabel"]
  E --> F
  F --> G["Pill label rendered"]
Loading
High-Level Assessment

The PR's approach—adding a shared style object and small presentational component reused across both the chat card subtitle and modal list—is a reasonable, low-risk way to keep label styling consistent. The concurrent migration from @material-ui/core makeStyles to @mui/material styled is a sensible cleanup opportunity bundled with the feature, though it slightly increases the diff surface for what is otherwise a small UI feature.

Files changed (8) +413 / -290

Enhancement (4) +151 / -84
RagSourceLabel.tsxNew RagSourceLabel component for pill-style source labels +45/-0

New RagSourceLabel component for pill-style source labels

• Adds a new reusable component and shared style object rendering the RAG source as a pill-shaped label used across source cards and the sources modal.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/RagSourceLabel.tsx

SourcesChipModal.tsxRender RagSourceLabel in sources modal and migrate to styled components +104/-84

Render RagSourceLabel in sources modal and migrate to styled components

• Converts makeStyles-based styling to MUI styled components and adds the RagSourceLabel next to each source title using the new subtitle field.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/SourcesChipModal.tsx

types.tsAdd optional source field to ReferencedDocument type +1/-0

Add optional source field to ReferencedDocument type

• Extends the ReferencedDocument type with an optional 'source' field to carry the LCORE knowledge base identifier.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/types.ts

lightspeed-chatbox-utils.tsxMap document source field to subtitle in transformDocumentsToSources +1/-0

Map document source field to subtitle in transformDocumentsToSources

• Updates the transform utility to pass the document's 'source' field through as 'subtitle' on the resulting SourcesCardProps.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/utils/lightspeed-chatbox-utils.tsx

Refactor (1) +222 / -206
LightspeedChatBox.tsxMigrate styling to MUI styled API and apply RAG source label styling +222/-206

Migrate styling to MUI styled API and apply RAG source label styling

• Replaces '@material-ui/core' makeStyles with '@mui/material' styled components and GlobalStyles for keyframes, and applies the shared ragSourceLabelAppearance to the sources card subtitle to render it as a pill label.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx

Tests (2) +35 / -0
SourcesChipModal.test.tsxAdd test coverage for RAG source labels in modal +11/-0

Add test coverage for RAG source labels in modal

• Adds mock subtitle data and a test asserting that RAG source labels render next to document titles in the sources modal.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/tests/SourcesChipModal.test.tsx

lightspeed-chatbot-utils.test.tsxAdd tests for source-to-subtitle mapping +24/-0

Add tests for source-to-subtitle mapping

• Adds test cases verifying that the 'source' field maps to 'subtitle' in transformed sources and is omitted when absent.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/utils/tests/lightspeed-chatbot-utils.test.tsx

Documentation (1) +5 / -0
rag-source-labels.mdAdd changeset for RAG source label feature +5/-0

Add changeset for RAG source label feature

• Adds a patch changeset describing the new label showing the LCORE source field on referenced document cards.

workspaces/intelligent-assistant/.changeset/rag-source-labels.md

@rhdh-qodo-merge

rhdh-qodo-merge Bot commented Aug 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. :has() padding dependency ✓ Resolved 🐞 Bug ☼ Reliability
Description
StyledMessageBox uses a :has(.pf-chatbot__sources-card-subtitle) selector to add padding, while
positioning the subtitle absolutely; in environments without :has() support, the padding rule is
ignored and the label can overlap the title text. This is a visual correctness issue for
older/incomplete CSS selector engines.
Code

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[R70-73]

+    '& .pf-chatbot__sources-card-title-container:has(.pf-chatbot__sources-card-subtitle)':
+      {
+        paddingRight: 'var(--pf-t--global--spacer--4xl)',
+        paddingTop: 'var(--pf-t--global--spacer--sm)',
Relevance

●● Moderate

No clear repo precedent on :has() fallbacks; team has mixed tolerance for fragile PF/CSS selector
patterns.

PR-#3366

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code makes .pf-chatbot__sources-card-subtitle absolutely positioned, and only
conditionally adds padding to the title container via :has(). If that selector is ignored, the
absolute element can overlap the unpadded title region.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[65-81]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`StyledMessageBox` relies on a `:has()` selector to conditionally apply padding to the source-card title container when a subtitle exists. If `:has()` is unsupported, the rule is dropped and the absolutely-positioned subtitle pill may overlap the title.

### Issue Context
The subtitle is positioned absolutely (`top/right`) and needs reserved space in the title container.

### Fix Focus Areas
- workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[67-81]

### Suggested fix approach
Implement a fallback that does not depend on `:has()`, e.g.:
- Apply `paddingRight`/`paddingTop` to `.pf-chatbot__sources-card-title-container` unconditionally (small extra padding when no subtitle), **or**
- Use `@supports selector(:has(*))` to keep the conditional rule for supporting browsers and add a conservative default padding rule for others.
This ensures the subtitle pill never overlaps the title even when `:has()` is unavailable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Inline GlobalStyles object ✓ Resolved 🐞 Bug ➹ Performance
Description
LightspeedChatBox creates a new object literal for GlobalStyles.styles on every render, adding
avoidable style-serialization work during frequent re-renders (e.g., streaming messages).
Hoisting/memoizing the static styles reduces overhead and keeps render output stable.
Code

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[R217-220]

+        <GlobalStyles
+          styles={{
+            [`@keyframes ${DEEP_THINKING_CLASS}`]: {
+              '0%': { opacity: 0.65 },
Relevance

● Weak

Similar “hoist/memoize inline render objects” perf nits were rejected as unnecessary
micro-optimizations.

PR-#3610

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The GlobalStyles component is rendered inside LightspeedChatBox with an inline object literal
for styles, which is necessarily recreated on each render.

workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[215-228]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`<GlobalStyles styles={{ ... }} />` is built inline in the component render path, so the `styles` object reference changes every render.

### Issue Context
Chat UIs re-render frequently (streaming tokens, scrolling state), so it’s beneficial to keep static style definitions stable.

### Fix Focus Areas
- workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightspeedChatBox.tsx[215-228]

### Suggested fix approach
Either:
1) Hoist the `styles` object to a module-level constant (since it only depends on `DEEP_THINKING_CLASS`), or
2) Wrap it with `useMemo` so the object is stable across renders.
Then pass that stable object to `GlobalStyles`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 11 rules
✅ Cross-repo context
  Not relevant to this PR: redhat-developer/rhdh
  Not relevant to this PR: redhat-developer/rhdh-chart
  Not relevant to this PR: redhat-developer/rhdh-operator
  Not relevant to this PR: redhat-developer/rhdh-local

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

@rhdh-qodo-merge rhdh-qodo-merge Bot added enhancement New feature or request Tests labels Aug 5, 2026
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.34%. Comparing base (e49bc01) to head (971faa9).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4178   +/-   ##
=======================================
  Coverage   58.33%   58.34%           
=======================================
  Files        2432     2433    +1     
  Lines       96775    96793   +18     
  Branches    26885    26886    +1     
=======================================
+ Hits        56456    56474   +18     
  Misses      38852    38852           
  Partials     1467     1467           
Flag Coverage Δ *Carryforward flag
adoption-insights 84.55% <ø> (ø) Carriedforward from e49bc01
ai-integrations 69.76% <ø> (ø) Carriedforward from e49bc01
app-defaults 69.79% <ø> (ø) Carriedforward from e49bc01
augment 46.67% <ø> (ø) Carriedforward from e49bc01
boost 76.77% <ø> (ø) Carriedforward from e49bc01
bulk-import 72.79% <ø> (ø) Carriedforward from e49bc01
cost-management 13.55% <ø> (ø) Carriedforward from e49bc01
dcm 67.21% <ø> (ø) Carriedforward from e49bc01
extensions 56.59% <ø> (ø) Carriedforward from e49bc01
global-floating-action-button 71.18% <ø> (ø) Carriedforward from e49bc01
global-header 66.50% <ø> (ø) Carriedforward from e49bc01
homepage 47.59% <ø> (ø) Carriedforward from e49bc01
install-dynamic-plugins 59.95% <ø> (ø) Carriedforward from e49bc01
intelligent-assistant 75.31% <100.00%> (+0.07%) ⬆️
konflux 91.98% <ø> (ø) Carriedforward from e49bc01
lightspeed 69.02% <ø> (ø) Carriedforward from e49bc01
mcp-integrations 83.40% <ø> (ø) Carriedforward from e49bc01
orchestrator 66.91% <ø> (ø) Carriedforward from e49bc01
quickstart 63.74% <ø> (ø) Carriedforward from e49bc01
sandbox 79.56% <ø> (ø) Carriedforward from e49bc01
scorecard 86.17% <ø> (ø) Carriedforward from e49bc01
theme 88.77% <ø> (ø) Carriedforward from e49bc01
translations 5.12% <ø> (ø) Carriedforward from e49bc01
x2a 79.20% <ø> (ø) Carriedforward from e49bc01

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e49bc01...971faa9. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ShiranHi

ShiranHi commented Aug 6, 2026

Copy link
Copy Markdown

Looks good to me, thank you @debsmita1 !

@its-mitesh-kumar

Copy link
Copy Markdown
Member

@debsmita1 I verified by brining my own knowledge, the response is good. The url and tag is proper in the card is proper , but the name is not appearing. Could you please check it.

S_ 2026-08-06 at 4 50 57 PM (2)

@its-mitesh-kumar

its-mitesh-kumar commented Aug 6, 2026

Copy link
Copy Markdown
Member

Local BYOK Testing Guide

Prerequisites

  • podman or docker installed
  • uv (Python package manager) installed
  • ollama installed and running (ollama serve)
  • Clone of rhdh-rag-content
  • Clone of lightspeed-configs (or equivalent local setup)

Step 1: Prepare your knowledge source

Create a working directory and add your document with YAML frontmatter for citation support:

mkdir -p ~/Desktop/redhat/byok-test/docs

Create ~/Desktop/redhat/byok-test/docs/my-knowledge.md:

---
title: My Custom Knowledge Document
url: https://example.com/my-doc
---

# Your content here
...

Step 2: Set up the Python environment

cd ~/Desktop/redhat/rhdh-rag-content
uv sync
source .venv/bin/activate

Step 3: Download the embedding model

python -c "
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')
model.save('$HOME/Desktop/redhat/byok-test/embeddings_model')
print('Done — model saved.')
"

Step 4: Create the custom processor

Create ~/Desktop/redhat/byok-test/custom_processor.py:

#!/usr/bin/env python3
"""Generate BYOK vector database from custom knowledge sources."""

import os
from lightspeed_rag_content.metadata_processor import MetadataProcessor
from lightspeed_rag_content.document_processor import DocumentProcessor
from lightspeed_rag_content import utils


class CustomMetadataProcessor(MetadataProcessor):

    def __init__(self, url):
        self.url = url

    def url_function(self, file_path: str) -> str:
        return self.url


if __name__ == "__main__":
    parser = utils.get_common_arg_parser()
    args = parser.parse_args()

    metadata_processor = CustomMetadataProcessor(
        "https://example.com/my-doc",  # fallback URL for docs without frontmatter
    )

    document_processor = DocumentProcessor(
        args.chunk, args.overlap, args.model_name, args.model_dir,
        args.workers, args.vector_store_type,
    )

    try:
        document_processor.process(args.folder, metadata=metadata_processor)
        document_processor.save(args.index, args.output)
    finally:
        if hasattr(document_processor, "db") and hasattr(document_processor.db, "tmp_dir"):
            document_processor.db.tmp_dir.cleanup()

    print("Done — vector database saved.")
    os._exit(0)

Step 5: Build the vector index

cd ~/Desktop/redhat/byok-test

python custom_processor.py \
  -f ./docs \
  -o ./vector_db/my_knowledge \
  -i v1 \
  --vector-store-type=llamastack-faiss \
  -md ./embeddings_model \
  -mn sentence-transformers/all-mpnet-base-v2 \
  --chunk 512 \
  --overlap 128

Step 6: Extract the vector_db_id

python -c "
import sqlite3, json
conn = sqlite3.connect('./vector_db/my_knowledge/faiss_store.db')
rows = conn.execute('SELECT key FROM kvstore').fetchall()
for r in rows:
    if 'vector_stores' in r[0]:
        vid = r[0].split('::')[-1]
        print(f'vector_db_id: {vid}')
        break
conn.close()
"

Note the printed vector_db_id (e.g., vs_bb99eb5b-44df-4ccb-b221-8cbf70666b75).

Step 7: Copy BYOK data into the rag-content volume

cp -r ~/Desktop/redhat/byok-test/vector_db/my_knowledge \
      ~/Desktop/redhat/lightspeed-configs/rag-content/vector_db/

Step 8: Configure lightspeed-stack.yaml

Add/update the byok_rag and rag sections in lightspeed-configs/lightspeed-core-configs/lightspeed-stack.yaml:

byok_rag:
  - rag_id: my-knowledge
    rag_type: inline::faiss
    embedding_model: sentence-transformers//rag-content/embeddings_model
    embedding_dimension: 768
    vector_db_id: <your-vector-db-id-from-step-6>
    db_path: /rag-content/vector_db/my_knowledge/faiss_store.db
    score_multiplier: 1.2

rag:
  inline:
    - my-knowledge       # deterministic — always injects context
  tool:
    - my-knowledge       # probabilistic — LLM calls file_search on demand

Step 9: Restart LCORE

cd ~/Desktop/redhat/lightspeed-configs
make local-down && make local-up

Wait ~30 seconds, then verify:

# Check container is healthy
podman ps --format '{{.Status}} {{.Names}}' | grep lightspeed

# Check readiness
curl -s http://localhost:8080/readiness

# Check models are registered
curl -s http://localhost:8080/v1/models | python3 -m json.tool

Step 10: Start the Backstage frontend and test

cd <rhdh-plugins>/workspaces/intelligent-assistant
yarn install
yarn start

Open http://localhost:3000, select an LLM model, and ask a question that should trigger your BYOK source. Verify:

  • Source cards show the correct rag_id label (e.g., my-knowledge)
  • The sources modal lists documents with the label next to each title
  • Citation links point to the URL from your document's frontmatter

Troubleshooting

Issue Fix
Could not load models LCORE not running — check podman ps -a and podman logs
Container exits with code 132 (SIGILL) Image architecture mismatch — check podman image inspect <image> --format '{{.Architecture}}'
Container exits with code 1 (validation error) Config schema mismatch — check podman logs for Extra inputs are not permitted
No BYOK results Verify vector_db_id matches, db_path is correct, and embedding model matches between build and query
Provider not found Comment out mcp_servers section if the provider isn't available in your LCORE version

@debsmita1 @HusneShabbir

@its-mitesh-kumar its-mitesh-kumar left a comment

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.

If not possible to get rid of PF styles override, then it can be accepted.

top: 'var(--pf-t--global--spacer--sm)',
right: 'var(--pf-t--global--spacer--sm)',
zIndex: 1,
...ragSourceLabelAppearance,

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.

These overrides are always been an headache. I hope you have added it as minimum as possible.

@maysunfaisal

Copy link
Copy Markdown
Contributor
Screen.Recording.2026-08-06.at.4.11.25.PM.mov

@its-mitesh-kumar its-mitesh-kumar left a comment

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.

The doc_title: "---" was not a PR bug — it was a document preparation issue. The rag-content processor uses the first line of the markdown file as the title, and since the source document started with YAML frontmatter (---), that delimiter became the title. Removing the frontmatter and rebuilding the vector index fixed it.
/lgtm

S_.2026-08-07.at.5.54.21.PM.mov

@openshift-ci openshift-ci Bot removed the lgtm label Aug 7, 2026
@openshift-ci

openshift-ci Bot commented Aug 7, 2026

Copy link
Copy Markdown

New changes are detected. LGTM label has been removed.

@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants