From 7614b8ae57f7fb22113b7bff07762a84f07b8ce4 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:11:58 -0400 Subject: [PATCH 01/12] UI fixes Signed-off-by: Lucas --- .../src/service/notebooks/notebooksRouters.ts | 5 ++-- .../lightspeed/plugins/lightspeed/config.d.ts | 30 +++++++++++++++++++ .../src/components/LightSpeedChat.tsx | 11 +++++-- .../src/components/notebooks/NotebookView.tsx | 10 +++++-- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts index a00e02a9b5..c4bb6f5c85 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts +++ b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts @@ -496,9 +496,9 @@ export async function createNotebooksRouter( tools: [{ type: 'file_search', vector_store_ids: [sessionId] }], model: `${queryProvider}/${queryModel}`, stream: true, - temperature: 0.05, + temperature: 0.35, shield_ids: [], - max_tool_calls: 10, + max_tool_calls: 15, ...(conversationId && { conversation: conversationId }), }; @@ -558,6 +558,7 @@ export async function createNotebooksRouter( .pipe(createResponsesApiTransform(session, sessionId, userId)) .pipe(res); } + console.log('response1234', response.body); break; } }), diff --git a/workspaces/lightspeed/plugins/lightspeed/config.d.ts b/workspaces/lightspeed/plugins/lightspeed/config.d.ts index 91b77d4815..2dab2341b9 100644 --- a/workspaces/lightspeed/plugins/lightspeed/config.d.ts +++ b/workspaces/lightspeed/plugins/lightspeed/config.d.ts @@ -36,5 +36,35 @@ export interface Config { */ message: string; }>; + /** + * Configuration for AI Notebooks + * @visibility frontend + */ + notebooks?: { + /** + * Enable/disable AI Notebooks feature + * When enabled, exposes AI Notebooks REST API endpoints for document-based conversations with RAG. + * Requires Lightspeed service to be running (default: http://0.0.0.0:8080). + * @default false + * @visibility frontend + */ + enabled: boolean; + /** + * Query configuration for notebooks + * @visibility frontend + */ + queryDefaults?: { + /** + * Model to use for answering queries + * @visibility frontend + */ + model: string; + /** + * AI provider for the query model + * @visibility frontend + */ + provider_id: string; + }; + }; }; } diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index ce2ae86381..13bd48d364 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -31,6 +31,8 @@ import { } from 'react-dropzone'; import { useMatch, useNavigate } from 'react-router-dom'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; + import { Button, makeStyles } from '@material-ui/core'; import { Chatbot, @@ -475,6 +477,9 @@ export const LightspeedChat = ({ const classes = useStyles(); const { t } = useTranslation(); const navigate = useNavigate(); + const configApi = useApi(configApiRef); + const notebooksEnabled = + configApi.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; const notebooksRouteMatch = useMatch('/lightspeed/notebooks'); const notebookViewRouteMatch = useMatch('/lightspeed/notebooks/:notebookId'); const routeNotebookId = notebookViewRouteMatch?.params?.notebookId; @@ -558,7 +563,7 @@ export const LightspeedChat = ({ } = useLightspeedDrawerContext(); const isFullscreenMode = displayMode === ChatbotDisplayMode.embedded; const showChatPanel = !isFullscreenMode || activeTab === 0; - const showNotebooksPanel = isFullscreenMode && activeTab !== 0; + const showNotebooksPanel = notebooksEnabled && activeTab !== 0; const [isChatHistoryDrawerOpen, setIsChatHistoryDrawerOpen] = useState(!isMobile && isFullscreenMode); @@ -1682,7 +1687,9 @@ export const LightspeedChat = ({ className={classes.tabs} > - + {notebooksEnabled && ( + + )}
diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx index 2f9dd1ca28..b63285167c 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx @@ -16,7 +16,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { useApi } from '@backstage/core-plugin-api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { makeStyles, Typography } from '@material-ui/core'; import { @@ -223,9 +223,15 @@ export const NotebookView = ({ const classes = useStyles(); const { t } = useTranslation(); const queryClient = useQueryClient(); + const configApi = useApi(configApiRef); const notebooksApi = useApi(notebooksApiRef); const { mutateAsync: notebookCreateMessage } = useCreateNotebookMessage(); + // Use notebook-specific model from config instead of chat's selected model + const notebookModel = configApi.getString( + 'lightspeed.notebooks.queryDefaults.model', + ); + const [conversationId, setConversationId] = useState( metadata?.conversation_id ?? TEMP_CONVERSATION_ID, ); @@ -285,7 +291,7 @@ export const NotebookView = ({ useConversationMessages( conversationId, userName, - selectedModel, + notebookModel, '', avatar, onComplete, From c5ce7ff5b1e5e5a0f3896ee3bf9ace35ad480a55 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:32:47 -0400 Subject: [PATCH 02/12] filter out notebook conversations Signed-off-by: Lucas --- .../lightspeed-backend/src/service/router.ts | 37 +++++++++++++++++ .../lightspeed/src/api/LightspeedApiClient.ts | 14 +++++++ .../plugins/lightspeed/src/api/api.ts | 1 + .../src/components/LightSpeedChat.tsx | 13 +++--- .../plugins/lightspeed/src/hooks/index.ts | 1 + .../src/hooks/useNotebookConversationIds.ts | 41 +++++++++++++++++++ 6 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 workspaces/lightspeed/plugins/lightspeed/src/hooks/useNotebookConversationIds.ts diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts index 692741ef1d..8b5a472baa 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts +++ b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts @@ -413,6 +413,43 @@ export async function createRouter( } }); + // Returns conversation IDs associated with notebook sessions for filtering + router.get('/notebook-conversation-ids', async (req, res) => { + try { + const credentials = await httpAuth.credentials(req); + const user = await userInfo.getUserInfo(credentials); + const userId = user.userEntityRef; + + const vectorStoresPage = await vectorStoresOperator.vectorStores.list(); + const vectorStores = vectorStoresPage.data || []; + + const conversationIds: string[] = []; + + for (const store of vectorStores) { + const sessionUserId = store.metadata?.user_id as string; + const conversationId = store.metadata?.conversation_id as string | null; + + // Only include this user's sessions with a conversation_id + if (sessionUserId === userId && conversationId) { + conversationIds.push(conversationId); + } + } + + res.json({ + conversation_ids: conversationIds, + }); + } catch (error) { + const errormsg = `Error fetching notebook conversation IDs: ${error}`; + logger.error(errormsg); + + if (error instanceof NotAllowedError) { + res.status(403).json({ error: error.message }); + } else { + res.status(500).json({ error: errormsg }); + } + } + }); + // ─── Proxy Middleware (existing) ──────────────────────────────────── router.use('/', async (req, res, next) => { diff --git a/workspaces/lightspeed/plugins/lightspeed/src/api/LightspeedApiClient.ts b/workspaces/lightspeed/plugins/lightspeed/src/api/LightspeedApiClient.ts index a07229f302..a6cb6efe85 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/api/LightspeedApiClient.ts +++ b/workspaces/lightspeed/plugins/lightspeed/src/api/LightspeedApiClient.ts @@ -183,6 +183,20 @@ export class LightspeedApiClient implements LightspeedAPI { return response.conversations ?? []; } + async getNotebookConversationIds() { + const baseUrl = await this.getBaseUrl(); + const result = await this.fetcher(`${baseUrl}/notebook-conversation-ids`); + + if (!result.ok) { + throw new Error( + `failed to get notebook conversation IDs, status ${result.status}: ${result.statusText}`, + ); + } + + const response = await result.json(); + return response.conversation_ids ?? []; + } + async stopMessage(requestId: string): Promise<{ success: boolean }> { const baseUrl = await this.getBaseUrl(); const response = await this.fetchApi.fetch( diff --git a/workspaces/lightspeed/plugins/lightspeed/src/api/api.ts b/workspaces/lightspeed/plugins/lightspeed/src/api/api.ts index d5491bfea8..88966b3aff 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/api/api.ts +++ b/workspaces/lightspeed/plugins/lightspeed/src/api/api.ts @@ -47,6 +47,7 @@ export type LightspeedAPI = { newName: string, ) => Promise<{ success: boolean }>; getConversations: () => Promise; + getNotebookConversationIds: () => Promise; getFeedbackStatus: () => Promise; captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>; isTopicRestrictionEnabled: () => Promise; diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index 13bd48d364..ad94d0f75d 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -89,6 +89,7 @@ import { useLastOpenedConversation, useLightspeedDeletePermission, useLightspeedNotebooksPermission, + useNotebookConversationIds, useNotebookSession, useNotebookSessions, usePinnedChatsSettings, @@ -493,6 +494,9 @@ export const LightspeedChat = ({ useLightspeedNotebooksPermission(); const notebooksPermissionResolved = !notebooksPermissionLoading && hasNotebooksAccess; + + const { data: notebookConversationIdsArray = [] } = + useNotebookConversationIds(); const { data: notebooks = [], refetch: refetchNotebooks } = useNotebookSessions(notebooksPermissionResolved); const hasNotebooks = notebooks.length > 0; @@ -989,13 +993,8 @@ export const LightspeedChat = ({ ); const notebookConversationIds = useMemo( - () => - new Set( - notebooks - .map(n => n.metadata?.conversation_id) - .filter((id): id is string => !!id), - ), - [notebooks], + () => new Set(notebookConversationIdsArray), + [notebookConversationIdsArray], ); const chatOnlyConversations = useMemo( diff --git a/workspaces/lightspeed/plugins/lightspeed/src/hooks/index.ts b/workspaces/lightspeed/plugins/lightspeed/src/hooks/index.ts index b081503761..c60855f7c3 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/hooks/index.ts +++ b/workspaces/lightspeed/plugins/lightspeed/src/hooks/index.ts @@ -25,6 +25,7 @@ export * from './useLastOpenedConversation'; export * from './useLightspeedDeletePermission'; export * from './notebooks/useLightspeedNotebooksPermission'; export * from './useLightspeedViewPermission'; +export * from './useNotebookConversationIds'; export * from './useDisplayModeSettings'; export * from './notebooks/useNotebookSession'; export * from './notebooks/useNotebookSessions'; diff --git a/workspaces/lightspeed/plugins/lightspeed/src/hooks/useNotebookConversationIds.ts b/workspaces/lightspeed/plugins/lightspeed/src/hooks/useNotebookConversationIds.ts new file mode 100644 index 0000000000..dc49594ed3 --- /dev/null +++ b/workspaces/lightspeed/plugins/lightspeed/src/hooks/useNotebookConversationIds.ts @@ -0,0 +1,41 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useApi } from '@backstage/core-plugin-api'; + +import { useQuery, type UseQueryResult } from '@tanstack/react-query'; + +import { lightspeedApiRef } from '../api/api'; + +/** + * Hook to fetch conversation IDs associated with notebook sessions for filtering + * Works even when notebooks feature is disabled + */ +export const useNotebookConversationIds = (): UseQueryResult< + string[], + Error +> => { + const lightspeedApi = useApi(lightspeedApiRef); + + return useQuery({ + queryKey: ['notebookConversationIds'], + queryFn: async () => { + return await lightspeedApi.getNotebookConversationIds(); + }, + staleTime: 1000 * 60 * 5, // 5 minutes + retry: false, + }); +}; From ed50d2e424423590dac8b2ea41c7f4c2209d0bf2 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:36:08 -0400 Subject: [PATCH 03/12] adding changeset Signed-off-by: Lucas --- workspaces/lightspeed/.changeset/late-beers-taste.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 workspaces/lightspeed/.changeset/late-beers-taste.md diff --git a/workspaces/lightspeed/.changeset/late-beers-taste.md b/workspaces/lightspeed/.changeset/late-beers-taste.md new file mode 100644 index 0000000000..b9be5da0c0 --- /dev/null +++ b/workspaces/lightspeed/.changeset/late-beers-taste.md @@ -0,0 +1,6 @@ +--- +'@red-hat-developer-hub/backstage-plugin-lightspeed-backend': minor +'@red-hat-developer-hub/backstage-plugin-lightspeed': minor +--- + +Hide notebooks tab when `lightspeed.notebooks.enabled: false` in config, Fix notebook queries to display correct model from config instead of chat's selected model, Add `/notebook-conversation-ids` endpoint to filter notebook conversations from chat list even when notebooks disabled From ac757eda847e0ed836608a5515c40d0ec46f66f6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:39:06 -0400 Subject: [PATCH 04/12] clean up changesets Signed-off-by: Lucas --- workspaces/lightspeed/.changeset/late-beers-taste.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workspaces/lightspeed/.changeset/late-beers-taste.md b/workspaces/lightspeed/.changeset/late-beers-taste.md index b9be5da0c0..4070b0290f 100644 --- a/workspaces/lightspeed/.changeset/late-beers-taste.md +++ b/workspaces/lightspeed/.changeset/late-beers-taste.md @@ -3,4 +3,6 @@ '@red-hat-developer-hub/backstage-plugin-lightspeed': minor --- -Hide notebooks tab when `lightspeed.notebooks.enabled: false` in config, Fix notebook queries to display correct model from config instead of chat's selected model, Add `/notebook-conversation-ids` endpoint to filter notebook conversations from chat list even when notebooks disabled +- Hide notebooks tab when `lightspeed.notebooks.enabled: false` in config +- Fix notebook queries to display correct model from config instead of chat's selected model +- Add `/notebook-conversation-ids` endpoint to filter notebook conversations from chat list even when notebooks disabled From c64e6c5b59c18058b9bf206ae01851f16d97f4ca Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:39:55 -0400 Subject: [PATCH 05/12] more cleanup Signed-off-by: Lucas --- workspaces/lightspeed/app-config.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/workspaces/lightspeed/app-config.yaml b/workspaces/lightspeed/app-config.yaml index cef0853485..2f65ace298 100644 --- a/workspaces/lightspeed/app-config.yaml +++ b/workspaces/lightspeed/app-config.yaml @@ -16,12 +16,13 @@ app: organization: name: Red Hat +# Disable AI Notebooks feature by default lightspeed: notebooks: - enabled: true + enabled: false queryDefaults: - model: llama3.2:3b - provider_id: vllm + model: ${NOTEBOOKS_QUERY_MODEL} + provider_id: ${NOTEBOOKS_QUERY_PROVIDER_ID} backend: # Used for enabling authentication, secret is shared by all backend plugins From aa812b4c18826c34bbed3317efa54af1739ee31b Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 6 May 2026 16:45:21 -0400 Subject: [PATCH 06/12] deleting unused variable Signed-off-by: Lucas --- .../plugins/lightspeed/src/components/LightSpeedChat.tsx | 1 - .../lightspeed/src/components/notebooks/NotebookView.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index 6d067e4a47..0818d67762 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -1859,7 +1859,6 @@ export const LightspeedChat = ({ avatar={avatar} profileLoading={profileLoading} topicRestrictionEnabled={topicRestrictionEnabled} - selectedModel={selectedModel} onClose={handleCloseNotebook} /> )} diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx index b63285167c..c01cbcc829 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx @@ -203,7 +203,6 @@ type NotebookViewProps = { avatar?: string; profileLoading: boolean; topicRestrictionEnabled: boolean; - selectedModel: string; onClose: () => void; }; @@ -217,7 +216,6 @@ export const NotebookView = ({ avatar, profileLoading, topicRestrictionEnabled, - selectedModel, onClose, }: NotebookViewProps) => { const classes = useStyles(); From 8292fb0bd31c25303d8563dee601a0a76f0e4166 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 7 May 2026 11:29:04 -0400 Subject: [PATCH 07/12] fix tests Signed-off-by: Lucas --- .../components/__tests__/LightspeedChat.test.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedChat.test.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedChat.test.tsx index eb0f063bdc..f66e4c5aba 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedChat.test.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedChat.test.tsx @@ -154,14 +154,28 @@ const mockUseLightspeedDrawerContext = typeof useLightspeedDrawerContext >; -const configAPi = mockApis.config({}); +const configAPi = mockApis.config({ + data: { + lightspeed: { + notebooks: { + enabled: true, + queryDefaults: { + model: 'gpt-4', + provider_id: 'openai', + }, + }, + }, + }, +}); const mockLightspeedApi = { getAllModels: jest.fn().mockResolvedValue([]), getConversationMessages: jest.fn().mockResolvedValue([]), createMessage: jest.fn().mockResolvedValue(new Response().body), deleteConversation: jest.fn().mockResolvedValue({ success: true }), + renameConversation: jest.fn().mockResolvedValue({ success: true }), getConversations: jest.fn().mockResolvedValue([]), + getNotebookConversationIds: jest.fn().mockResolvedValue([]), getFeedbackStatus: jest.fn().mockResolvedValue(false), captureFeedback: jest.fn().mockResolvedValue({ response: 'success' }), isTopicRestrictionEnabled: jest.fn().mockResolvedValue(false), From 09f78865249b2a0c0519fe8ab13982ad9133bea3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 7 May 2026 16:10:58 -0400 Subject: [PATCH 08/12] fix break when querydefualt is not defined Signed-off-by: Lucas --- workspaces/lightspeed/app-config.yaml | 2 +- workspaces/lightspeed/playwright.config.ts | 3 ++ .../plugins/lightspeed-backend/src/plugin.ts | 42 +++++++++++++------ .../src/service/notebooks/notebooksRouters.ts | 10 +---- .../plugins/lightspeed/src/const.ts | 6 --- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/workspaces/lightspeed/app-config.yaml b/workspaces/lightspeed/app-config.yaml index 2f65ace298..796b42f166 100644 --- a/workspaces/lightspeed/app-config.yaml +++ b/workspaces/lightspeed/app-config.yaml @@ -19,7 +19,7 @@ organization: # Disable AI Notebooks feature by default lightspeed: notebooks: - enabled: false + enabled: ${NOTEBOOKS_ENABLED:-false} queryDefaults: model: ${NOTEBOOKS_QUERY_MODEL} provider_id: ${NOTEBOOKS_QUERY_PROVIDER_ID} diff --git a/workspaces/lightspeed/playwright.config.ts b/workspaces/lightspeed/playwright.config.ts index 0e5e0acf2c..0ea916f40d 100644 --- a/workspaces/lightspeed/playwright.config.ts +++ b/workspaces/lightspeed/playwright.config.ts @@ -38,6 +38,9 @@ export default defineConfig({ port: 3000, reuseExistingServer: true, cwd: __dirname, + env: { + NOTEBOOKS_ENABLED: 'true', + }, }, retries: process.env.CI ? 2 : 0, diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/src/plugin.ts b/workspaces/lightspeed/plugins/lightspeed-backend/src/plugin.ts index 54527f1b87..c8aef55efa 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/src/plugin.ts +++ b/workspaces/lightspeed/plugins/lightspeed-backend/src/plugin.ts @@ -64,22 +64,38 @@ export const lightspeedPlugin = createBackendPlugin({ const aiNotebooksEnabled = config.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; + if (aiNotebooksEnabled) { - http.use( - await createNotebooksRouter({ - config: config, - logger: logger, - httpAuth: httpAuth, - userInfo: userInfo, - permissions, - }), + const queryModel = config.getOptionalString( + 'lightspeed.notebooks.queryDefaults.model', + ); + const queryProvider = config.getOptionalString( + 'lightspeed.notebooks.queryDefaults.provider_id', ); - logger.info('AI Notebooks enabled'); - http.addAuthPolicy({ - path: '/notebooks/health', - allow: 'unauthenticated', - }); + if (!queryModel || !queryProvider) { + logger.warn( + 'AI Notebooks feature is enabled but required configuration is missing. ' + + 'Please configure lightspeed.notebooks.queryDefaults.model and lightspeed.notebooks.queryDefaults.provider_id. ' + + 'Notebooks will not be available until these are set.', + ); + } else { + http.use( + await createNotebooksRouter({ + config: config, + logger: logger, + httpAuth: httpAuth, + userInfo: userInfo, + permissions, + }), + ); + logger.info('AI Notebooks enabled'); + + http.addAuthPolicy({ + path: '/notebooks/health', + allow: 'unauthenticated', + }); + } } // Configure authentication policies diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts index c4bb6f5c85..3b5278d4b7 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts +++ b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/notebooks/notebooksRouters.ts @@ -69,20 +69,14 @@ export async function createNotebooksRouter( config.getOptionalNumber('lightspeed.servicePort') ?? DEFAULT_LIGHTSPEED_SERVICE_PORT; const lightspeedBaseUrl = `http://${DEFAULT_LIGHTSPEED_SERVICE_HOST}:${lightSpeedPort}`; - const queryModel = config.getOptionalString( + const queryModel = config.getString( 'lightspeed.notebooks.queryDefaults.model', ); - const queryProvider = config.getOptionalString( + const queryProvider = config.getString( 'lightspeed.notebooks.queryDefaults.provider_id', ); const systemPrompt = NOTEBOOKS_SYSTEM_PROMPT; - if (!queryModel || !queryProvider) { - throw new Error( - 'Query model and provider are required. Please configure lightspeed.notebooks.queryDefaults.model and lightspeed.notebooks.queryDefaults.provider_id', - ); - } - logger.info( `AI Notebooks connecting to Lightspeed-Core at ${lightspeedBaseUrl}`, ); diff --git a/workspaces/lightspeed/plugins/lightspeed/src/const.ts b/workspaces/lightspeed/plugins/lightspeed/src/const.ts index 17a01e55d0..15c299814f 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/const.ts +++ b/workspaces/lightspeed/plugins/lightspeed/src/const.ts @@ -45,10 +45,6 @@ export const NOTEBOOK_ALLOWED_EXTENSIONS: Record = { '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 = { @@ -59,8 +55,6 @@ export const NOTEBOOK_EXTENSION_TO_FILE_TYPE: Record = { '.yaml': 'yaml', '.yml': 'yaml', '.log': 'log', - '.docx': 'txt', - '.odt': 'txt', }; export const DEFAULT_SAMPLE_PROMPTS: SamplePrompts = [ From 39411c812724699db4f91aefefe066211208eb55 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 7 May 2026 16:19:53 -0400 Subject: [PATCH 09/12] remove tab when notebook not enabled Signed-off-by: Lucas --- .../plugins/lightspeed/src/components/LightSpeedChat.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index 2ef229873f..7e32b126a0 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -1769,7 +1769,7 @@ export const LightspeedChat = ({ onMcpSettingsClick={() => setIsMcpSettingsOpen(true)} /> - {isFullscreenMode && ( + {isFullscreenMode && notebooksEnabled && ( <> - {notebooksEnabled && ( - - )} +
From 85405787b3e6c07fb3df0aef8a30fb3767a7585d Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 7 May 2026 18:38:34 -0400 Subject: [PATCH 10/12] fix UI test Signed-off-by: Lucas --- .../plugins/lightspeed/src/components/LightSpeedChat.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index 7e32b126a0..cdfc2a6a60 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -491,11 +491,13 @@ export const LightspeedChat = ({ const { t } = useTranslation(); const navigate = useNavigate(); const configApi = useApi(configApiRef); - const notebooksEnabled = - configApi.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; const notebooksRouteMatch = useMatch('/lightspeed/notebooks'); const notebookViewRouteMatch = useMatch('/lightspeed/notebooks/:notebookId'); const routeNotebookId = notebookViewRouteMatch?.params?.notebookId; + const notebooksEnabled = + configApi.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; + const shouldShowTabs = + notebooksEnabled || notebooksRouteMatch || notebookViewRouteMatch; const { displayMode, setDisplayMode, @@ -1769,7 +1771,7 @@ export const LightspeedChat = ({ onMcpSettingsClick={() => setIsMcpSettingsOpen(true)} /> - {isFullscreenMode && notebooksEnabled && ( + {isFullscreenMode && shouldShowTabs && ( <> Date: Thu, 7 May 2026 19:54:12 -0400 Subject: [PATCH 11/12] fix ui Signed-off-by: Lucas --- .../lightspeed/src/components/LightSpeedChat.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx index cdfc2a6a60..f2bc90ebca 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx @@ -491,13 +491,15 @@ export const LightspeedChat = ({ const { t } = useTranslation(); const navigate = useNavigate(); const configApi = useApi(configApiRef); + const notebooksEnabled = + configApi.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; const notebooksRouteMatch = useMatch('/lightspeed/notebooks'); const notebookViewRouteMatch = useMatch('/lightspeed/notebooks/:notebookId'); const routeNotebookId = notebookViewRouteMatch?.params?.notebookId; - const notebooksEnabled = - configApi.getOptionalBoolean('lightspeed.notebooks.enabled') ?? false; - const shouldShowTabs = - notebooksEnabled || notebooksRouteMatch || notebookViewRouteMatch; + const isOnNotebookRoute = Boolean( + notebooksRouteMatch || notebookViewRouteMatch, + ); + const shouldShowTabs = notebooksEnabled || isOnNotebookRoute; const { displayMode, setDisplayMode, @@ -599,9 +601,9 @@ export const LightspeedChat = ({ const wasStoppedByUserRef = useRef(false); const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } = useLastOpenedConversation(user); - // Chat vs Notebooks tabs are fullscreen-only; overlay and docked always show Chat. const showChatPanel = !isFullscreenMode || activeTab === 0; - const showNotebooksPanel = notebooksEnabled && activeTab !== 0; + const showNotebooksPanel = + (notebooksEnabled || isOnNotebookRoute) && activeTab !== 0; const [isChatHistoryDrawerOpen, setIsChatHistoryDrawerOpen] = useState(!isMobile && isFullscreenMode); From 94eca394af21245fbebbbc8063a32cbe59a99db3 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 8 May 2026 00:16:43 -0400 Subject: [PATCH 12/12] fix ci Signed-off-by: Lucas --- workspaces/lightspeed/app-config.yaml | 3 +++ workspaces/lightspeed/playwright.config.ts | 2 ++ .../lightspeed/src/components/notebooks/NotebookView.tsx | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/workspaces/lightspeed/app-config.yaml b/workspaces/lightspeed/app-config.yaml index 796b42f166..58105ce5e0 100644 --- a/workspaces/lightspeed/app-config.yaml +++ b/workspaces/lightspeed/app-config.yaml @@ -119,3 +119,6 @@ catalog: pullRequestBranchName: backstage-integration rules: - allow: [Component, System, API, Resource, Location] + locations: + - type: file + target: ./catalog-info.yaml diff --git a/workspaces/lightspeed/playwright.config.ts b/workspaces/lightspeed/playwright.config.ts index 0ea916f40d..2555d362a7 100644 --- a/workspaces/lightspeed/playwright.config.ts +++ b/workspaces/lightspeed/playwright.config.ts @@ -40,6 +40,8 @@ export default defineConfig({ cwd: __dirname, env: { NOTEBOOKS_ENABLED: 'true', + NOTEBOOKS_QUERY_MODEL: 'gpt-4', + NOTEBOOKS_QUERY_PROVIDER_ID: 'openai', }, }, diff --git a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx index 835297ec38..7bae7eaea0 100644 --- a/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx +++ b/workspaces/lightspeed/plugins/lightspeed/src/components/notebooks/NotebookView.tsx @@ -239,9 +239,9 @@ export const NotebookView = ({ const { mutateAsync: notebookCreateMessage } = useCreateNotebookMessage(); // Use notebook-specific model from config instead of chat's selected model - const notebookModel = configApi.getString( - 'lightspeed.notebooks.queryDefaults.model', - ); + const notebookModel = + configApi.getOptionalString('lightspeed.notebooks.queryDefaults.model') || + ''; const [conversationId, setConversationId] = useState( metadata?.conversation_id ?? TEMP_CONVERSATION_ID,