feat(lightspeed): notebook chat - #2754
Conversation
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
|
This pull request adds a new top-level directory under |
Review Summary by QodoAdd notebook chat feature with document management and UI components
WalkthroughsDescription• Implemented comprehensive notebook chat feature with document management capabilities • Added NotebooksApiClient with new methods for session and document operations: createSession, uploadDocument, listDocuments, deleteDocument, getDocumentStatus, and querySession • Extended NotebooksAPI interface with document operations and session management • Created notebook-specific types for documents, sessions, and responses • Implemented file upload validation utilities with support for file type, size, and count constraints • Added React Query hooks for notebook operations: useCreateNotebook, useUploadDocument, useDocumentStatusPolling, useNotebookDocuments, and useCreateNotebookMessage • Built complete UI components for notebook functionality: NotebookView, AddDocumentModal, DocumentSidebar, OverwriteConfirmModal, FileTypeIcon, UploadResourceScreen • Integrated notebook view into main LightSpeedChat component with notebook creation and selection • Added comprehensive test coverage for utilities, hooks, and components • Provided translations in 6 languages (English, Japanese, German, French, Spanish, Italian) for all notebook UI elements • Enhanced useConversationMessages hook with createMessageOverride parameter for custom message creation logic • Configured backend notebook query defaults and metadata handling Diagramflowchart LR
A["NotebooksApiClient"] -- "session & document methods" --> B["NotebooksAPI Interface"]
B -- "uses" --> C["Notebook Types"]
D["File Upload Utils"] -- "validates" --> E["AddDocumentModal"]
E -- "uploads via" --> A
F["useCreateNotebook"] -- "creates" --> A
G["useUploadDocument"] -- "uploads" --> A
H["useDocumentStatusPolling"] -- "tracks" --> A
I["useNotebookDocuments"] -- "lists" --> A
J["useCreateNotebookMessage"] -- "queries" --> A
E --> K["NotebookView"]
L["DocumentSidebar"] -- "displays" --> K
M["OverwriteConfirmModal"] -- "confirms" --> K
K -- "integrated into" --> N["LightSpeedChat"]
O["Translations"] -- "supports" --> K
File Changes1. workspaces/lightspeed/plugins/lightspeed/src/api/NotebooksApiClient.ts
|
Code Review by Qodo
|
| if (!response.ok) { | ||
| const reader = response.body.getReader(); | ||
| const { done, value } = await reader.read(); | ||
| const text = done ? '' : new TextDecoder('utf-8').decode(value); | ||
| const errorMessage = JSON.parse(text); | ||
| if (errorMessage?.error) { | ||
| throw new Error( | ||
| `failed to query notebook session: ${errorMessage.error}`, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return response.body.getReader(); | ||
| } |
There was a problem hiding this comment.
1. Stream reader locked twice 🐞 Bug ≡ Correctness
NotebooksApiClient.querySession() calls response.body.getReader() in the error path and then calls getReader() again to return a reader, which will throw because the stream is already locked and also may let non-OK responses proceed without throwing.
Agent Prompt
### Issue description
`NotebooksApiClient.querySession()` locks the response stream by calling `response.body.getReader()` in the error branch and then calls `getReader()` again, which will throw at runtime. It also doesn't reliably throw for non-OK responses.
### Issue Context
This method is used for notebook chat streaming; failed requests should surface a clean error without breaking the stream API.
### Fix Focus Areas
- workspaces/lightspeed/plugins/lightspeed/src/api/NotebooksApiClient.ts[205-236]
### Suggested fix
- If `!response.ok`, read the error via `await response.text()` (or reuse a single reader and `releaseLock()`), then `throw` unconditionally.
- Only call `response.body.getReader()` once (in the success path) and return that reader.
- Wrap JSON parsing of error text in `try/catch` and fall back to raw text when not JSON.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| export const NOTEBOOK_ALLOWED_EXTENSIONS: Record<string, string[]> = { | ||
| 'text/plain': ['.txt', '.log'], | ||
| 'text/markdown': ['.md'], | ||
| 'application/pdf': ['.pdf'], | ||
| 'application/json': ['.json'], | ||
| 'application/x-yaml': ['.yaml', '.yml'], | ||
| 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': [ | ||
| '.docx', | ||
| ], | ||
| 'application/vnd.oasis.opendocument.text': ['.odt'], | ||
| }; | ||
|
|
||
| export const NOTEBOOK_EXTENSION_TO_FILE_TYPE: Record<string, string> = { | ||
| '.txt': 'txt', | ||
| '.md': 'md', | ||
| '.pdf': 'pdf', | ||
| '.json': 'json', | ||
| '.yaml': 'yaml', | ||
| '.yml': 'yaml', | ||
| '.log': 'log', | ||
| '.docx': 'txt', | ||
| '.odt': 'txt', | ||
| }; |
There was a problem hiding this comment.
3. Docx/odt upload corrupted 🐞 Bug ≡ Correctness
The frontend accepts .docx/.odt and maps them to fileType 'txt', but the backend only supports parsing txt/md/log as UTF-8 text, so these binary formats will be ingested as garbage content.
Agent Prompt
### Issue description
The UI currently allows `.docx`/`.odt` uploads and maps them to `fileType='txt'`, but the backend parses `txt` as UTF-8 text, corrupting binary document content.
### Issue Context
Backend supported types are `md/txt/pdf/json/yaml/yml/log/url` and text parsing is `buffer.toString('utf-8')`.
### Fix Focus Areas
- workspaces/lightspeed/plugins/lightspeed/src/const.ts[40-62]
- workspaces/lightspeed/plugins/lightspeed/src/hooks/notebooks/useUploadDocument.ts[34-52]
- workspaces/lightspeed/plugins/lightspeed-backend/src/service/constant.ts[36-48]
- workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/documents/fileParser.ts[41-59]
### Suggested fix
Choose one:
1) **Disallow docx/odt**: remove `.docx`/`.odt` from `NOTEBOOK_ALLOWED_EXTENSIONS` and `NOTEBOOK_EXTENSION_TO_FILE_TYPE`, and update the upload modal `infoText` translations accordingly.
2) **Add real backend support**: extend `SupportedFileType` and implement docx/odt parsing (e.g., extract text) instead of treating as plain UTF-8 text.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| config.getOptionalString( | ||
| 'lightspeed.aiNotebooks.queryDefaults.provider_id', | ||
| ) ?? ''; | ||
|
|
There was a problem hiding this comment.
@JslYoon You were not reading the default models so I have added these code, let me know it looks good to you, or I can remove it.
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
|
@its-mitesh-kumar could you add some screenshots to this PR once they are ready? You can use this prototype as a reference to ensure the implementation matches the design. |
|
Important This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior. Changed Packages
|
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
| buffer += chunk.toString(); | ||
| const lines = buffer.split('\n'); | ||
| buffer = buffer.endsWith('\n') ? '' : lines.pop() || ''; | ||
| const blocks = buffer.split('\n\n'); |
There was a problem hiding this comment.
@JslYoon Please see if this changes looks good to you.
Notebook chat streaming: normalize SSE format to match chat tab
The notebook query endpoint proxies responses from Lightspeed Core, which uses the Responses API SSE format (event: response.output_text.delta / data: {...}). However, the frontend useConversationMessages hook expects the legacy streaming format (data: {"event": "token", "data": {"token": "..."}}), which is what the chat tab already uses.
Without this transform, the frontend received the entire response as a single blob instead of streaming token-by-token, because it couldn't parse the incoming SSE events.
Added a createResponsesApiTransform in notebooksRouters.ts that converts Responses API SSE events to the legacy format on the fly, so both chat and notebook tabs stream identically. The transform also captures the conversation_id from the first response.created event and persists it on the session.
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
|
@JslYoon As already informed Notebook document upload shows failure status despite successful upload; document-based Q&A not working. I have created the bug for the same. Please have a look. https://redhat.atlassian.net/browse/RHDHBUGS-3015 |
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
ciiay
left a comment
There was a problem hiding this comment.
Hi @its-mitesh-kumar , I tested locally and I had one concern. When I tried to upload 00001.log file, it failed for the first time I uploaded it, then I tried again. The second time it showed up in the file list but still I got another failed message in the alert model. Please refer the screenshot and the backend log I attached.
2026-04-27T03:15:42.130Z lightspeed error /v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status: Document not found: 00001.log Document not found: 00001.log cause=undefined name="NotFoundError" stack="NotFoundError: Document not found: 00001.log\n at DocumentService.getFileStatus (/Users/yicai/redhat/rhdh-plugins/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/documents/documentService.ts:222:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async <anonymous> (/Users/yicai/redhat/rhdh-plugins/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts:406:26)\n at async <anonymous> (/Users/yicai/redhat/rhdh-plugins/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts:150:9)"
2026-04-27T03:15:42.132Z rootHttpRouter info [2026-04-27T03:15:42.132Z] "GET /api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status HTTP/1.1" 404 67 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" type="incomingRequest" date="2026-04-27T03:15:42.132Z" method="GET" url="/api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status" status=404 httpVersion="1.1" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" contentLength=67 referrer="http://localhost:3000/"
2026-04-27T03:15:42.144Z lightspeed info Document "00001.log" (ID: 00001.log) upload started with file file-e90b0c9c42014317928e1401d00f4d11
2026-04-27T03:15:42.144Z lightspeed info Background upload succeeded: 00001.log
2026-04-27T03:15:43.178Z rootHttpRouter info [2026-04-27T03:15:43.178Z] "GET /api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status HTTP/1.1" 200 113 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" type="incomingRequest" date="2026-04-27T03:15:43.178Z" method="GET" url="/api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status" status=200 httpVersion="1.1" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" contentLength=113 referrer="http://localhost:3000/"
2026-04-27T03:16:10.825Z lightspeed info Parsing file 00001.log for fileType log
2026-04-27T03:16:10.845Z lightspeed info File created - id: file-1040ad3ed157440f8db2c5396fec332d, filename: 00001.log
2026-04-27T03:16:10.846Z rootHttpRouter info [2026-04-27T03:16:10.846Z] "PUT /api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents HTTP/1.1" 202 157 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" type="incomingRequest" date="2026-04-27T03:16:10.846Z" method="PUT" url="/api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents" status=202 httpVersion="1.1" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" contentLength=157 referrer="http://localhost:3000/"
2026-04-27T03:16:10.941Z lightspeed info Deleting document 00001.log from vs_ecbc8901-b073-4169-9256-4bef7a9228d0
2026-04-27T03:16:11.045Z lightspeed info Listing documents for session vs_ecbc8901-b073-4169-9256-4bef7a9228d0
2026-04-27T03:16:11.077Z rootHttpRouter info [2026-04-27T03:16:11.077Z] "GET /api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status HTTP/1.1" 304 0 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" type="incomingRequest" date="2026-04-27T03:16:11.077Z" method="GET" url="/api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents/00001.log/status" status=304 httpVersion="1.1" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" referrer="http://localhost:3000/"
2026-04-27T03:16:11.107Z lightspeed info Found 2 documents in session vs_ecbc8901-b073-4169-9256-4bef7a9228d0
2026-04-27T03:16:11.108Z rootHttpRouter info [2026-04-27T03:16:11.108Z] "GET /api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents HTTP/1.1" 200 459 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" type="incomingRequest" date="2026-04-27T03:16:11.108Z" method="GET" url="/api/lightspeed/ai-notebooks/v1/sessions/vs_ecbc8901-b073-4169-9256-4bef7a9228d0/documents" status=200 httpVersion="1.1" userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" contentLength=459 referrer="http://localhost:3000/"
2026-04-27T03:16:11.184Z lightspeed info Deleted document 00001.log (file file-e90b0c9c42014317928e1401d00f4d11) from session vs_ecbc8901-b073-4169-9256-4bef7a9228d0
2026-04-27T03:16:11.250Z lightspeed info Document "00001.log" (ID: 00001.log) upload started with file file-1040ad3ed157440f8db2c5396fec332d
2026-04-27T03:16:11.250Z lightspeed info Background upload succeeded: 00001.log
|
@ciiay I have already created the bug for document upload status api is not giving proper response. It need to be fixed from the backend.
|
@ShiranHi Screenrecording has been updated in the PR description. |
Thank you @its-mitesh-kumar , it looks great. We only need to add a confirmation modal when deleting a resource (Figma). |
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
karthikjeeyar
left a comment
There was a problem hiding this comment.
Overall looks great.
Just few small observation during testing.
New route needed for notebook conversation
- Every notebook chat should have its own Route and refresh should stay on the same details page
There was a problem hiding this comment.
@karthikjeeyar Tried improving the spacing. Take a pull and see.
Screen.Recording.2026-04-27.at.8.20.10.PM.mov
@ShiranHi Should I add back the attach file icon ? |
I’d prefer not to include it as it isn't reflected in the existing Figma files. Let's keep it simple for now. |
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
@karthikjeeyar I have already Bug for the same is here : https://redhat.atlassian.net/browse/RHDHBUGS-2986 |
|
Notebook's conversation showing the source as external link and clicking on it doesn't do anything:
For lightspeed use-case, the So please modify this line where it sets isExternal variable (always sets to true) to conditionally set The above suggestion will remove the external icon in this source link:
cc: @ShiranHi |
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
@karthikjeeyar Addressed.
2. Lightspeed Chat
|
|
|
All problems mentioned will be fixed in the following PRs @its-mitesh-kumar : |
* feat(lightspeed): create notebook flow Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * feat(lightspeed): creating new notebook Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * updating status of documents Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * updating the unit tests Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * handling overwrite in create flow Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * adding changeset Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * feat(lightspeed): adding chat Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * delete of document Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * formatting the query response Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * adding changeset Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * updating delete functionality Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * fixing width Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * improving spacing Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> * update doc_url Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com> --------- Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>










Description
Adds the Notebook Chat feature to the Lightspeed plugin, enabling users to interact with AI Notebooks through a chat interface.
Steps to test
a. Create a file
env/values.envwith below contentb. Pull the RAG content:
c.
OLLAMA_HOST=0.0.0.0 ollama served. Start the local API stack:
yarn start:legacyUI after changes
Screen.Recording.2026-04-24.at.11.29.23.PM.mov
Screen.Recording.2026-04-25.at.12.45.52.AM.mov
Screen.Recording.2026-04-25.at.12.49.07.AM.mov
✔️ Checklist