@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,13 +53,10 @@ async function createNotebooksRouter(options) {
53
53
  "general",
54
54
  logger
55
55
  );
56
- const requireNotebooksPermission = async (req, res, next) => {
56
+ const requirePermission = (permission) => async (req, res, next) => {
57
57
  try {
58
58
  const { credentials } = getIdentity.getIdentity(req);
59
- await authorizer.authorizeUser(
60
- backstagePluginIntelligentAssistantCommon.lightspeedNotebooksUsePermission,
61
- credentials
62
- );
59
+ await authorizer.authorizeUser(permission, credentials);
63
60
  next();
64
61
  } catch (error) {
65
62
  utils.handleError(logger, res, error, "Permission denied");
@@ -182,7 +179,7 @@ async function createNotebooksRouter(options) {
182
179
  notebooksRouter.post(
183
180
  "/v1/sessions",
184
181
  generalRateLimiter,
185
- requireNotebooksPermission,
182
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
186
183
  withAuth(async (req, res, userId) => {
187
184
  const { name, description, metadata } = req.body;
188
185
  if (!name) {
@@ -201,7 +198,7 @@ async function createNotebooksRouter(options) {
201
198
  notebooksRouter.get(
202
199
  "/v1/sessions",
203
200
  generalRateLimiter,
204
- requireNotebooksPermission,
201
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
205
202
  withAuth(async (_req, res, userId) => {
206
203
  const sessions = await sessionService$1.listSessions(userId);
207
204
  res.json(notebooksTypes.createSessionListResponse(sessions));
@@ -210,7 +207,7 @@ async function createNotebooksRouter(options) {
210
207
  notebooksRouter.get(
211
208
  "/v1/sessions/:sessionId",
212
209
  generalRateLimiter,
213
- requireNotebooksPermission,
210
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
214
211
  withAuth(async (req, res, userId) => {
215
212
  const { sessionId } = req.params;
216
213
  const session = await sessionService$1.readSession(sessionId, userId);
@@ -222,7 +219,7 @@ async function createNotebooksRouter(options) {
222
219
  notebooksRouter.put(
223
220
  "/v1/sessions/:sessionId",
224
221
  generalRateLimiter,
225
- requireNotebooksPermission,
222
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksManagePermission),
226
223
  withAuth(async (req, res, userId) => {
227
224
  const { sessionId } = req.params;
228
225
  const { name, description, metadata } = req.body;
@@ -239,7 +236,7 @@ async function createNotebooksRouter(options) {
239
236
  notebooksRouter.delete(
240
237
  "/v1/sessions/:sessionId",
241
238
  generalRateLimiter,
242
- requireNotebooksPermission,
239
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksManagePermission),
243
240
  withAuth(async (req, res, userId) => {
244
241
  const { sessionId } = req.params;
245
242
  await sessionService$1.deleteSession(sessionId, userId);
@@ -254,7 +251,7 @@ async function createNotebooksRouter(options) {
254
251
  notebooksRouter.get(
255
252
  "/v1/sessions/:sessionId/documents",
256
253
  generalRateLimiter,
257
- requireNotebooksPermission,
254
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
258
255
  requireSessionOwnership(),
259
256
  withAuth(async (req, res) => {
260
257
  const { sessionId } = req.params;
@@ -269,7 +266,7 @@ async function createNotebooksRouter(options) {
269
266
  notebooksRouter.put(
270
267
  "/v1/sessions/:sessionId/documents",
271
268
  expensiveRateLimiter,
272
- requireNotebooksPermission,
269
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
273
270
  constant.upload.single("file"),
274
271
  withAuth(async (req, res, userId) => {
275
272
  const { sessionId } = req.params;
@@ -300,10 +297,10 @@ async function createNotebooksRouter(options) {
300
297
  status: "processing",
301
298
  document_id: newTitle || title,
302
299
  session_id: sessionId,
303
- message: "Document upload started"
300
+ message: "Resource upload started"
304
301
  });
305
302
  const docName = newTitle || title;
306
- documentService$1.upsertDocument(sessionId, title, fileType, fileId, newTitle).then(() => logger.info(`Background upload succeeded: ${docName}`)).catch(
303
+ documentService$1.upsertDocument(sessionId, title, { fileType, fileId, newTitle }).then(() => logger.info(`Background upload succeeded: ${docName}`)).catch(
307
304
  (err) => logger.error(`Background upload failed: ${docName}`, err)
308
305
  );
309
306
  })
@@ -311,7 +308,7 @@ async function createNotebooksRouter(options) {
311
308
  notebooksRouter.get(
312
309
  "/v1/sessions/:sessionId/documents/:documentId/status",
313
310
  generalRateLimiter,
314
- requireNotebooksPermission,
311
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
315
312
  requireSessionOwnership(),
316
313
  withAuth(async (req, res) => {
317
314
  const { sessionId, documentId } = req.params;
@@ -327,10 +324,43 @@ async function createNotebooksRouter(options) {
327
324
  });
328
325
  })
329
326
  );
327
+ notebooksRouter.patch(
328
+ "/v1/sessions/:sessionId/documents/:documentId",
329
+ generalRateLimiter,
330
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksManagePermission),
331
+ requireSessionOwnership(),
332
+ withAuth(async (req, res) => {
333
+ const { sessionId, documentId } = req.params;
334
+ const { title } = req.body;
335
+ if (!title || typeof title !== "string" || !title.trim()) {
336
+ utils.handleError(logger, res, "title is required");
337
+ return;
338
+ }
339
+ const MAX_TITLE_LENGTH = 255;
340
+ if (title.trim().length > MAX_TITLE_LENGTH) {
341
+ utils.handleError(
342
+ logger,
343
+ res,
344
+ `title must be ${MAX_TITLE_LENGTH} characters or less`
345
+ );
346
+ return;
347
+ }
348
+ await documentService$1.upsertDocument(sessionId, documentId, {
349
+ newTitle: title.trim()
350
+ });
351
+ res.json(
352
+ notebooksTypes.createDocumentResponse(
353
+ title.trim(),
354
+ sessionId,
355
+ "Document renamed successfully"
356
+ )
357
+ );
358
+ })
359
+ );
330
360
  notebooksRouter.delete(
331
361
  "/v1/sessions/:sessionId/documents/:documentId",
332
362
  generalRateLimiter,
333
- requireNotebooksPermission,
363
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksManagePermission),
334
364
  requireSessionOwnership(),
335
365
  withAuth(async (req, res) => {
336
366
  const { sessionId, documentId } = req.params;
@@ -339,7 +369,7 @@ async function createNotebooksRouter(options) {
339
369
  notebooksTypes.createDocumentResponse(
340
370
  documentId,
341
371
  sessionId,
342
- "Document deleted successfully"
372
+ "Resource deleted successfully"
343
373
  )
344
374
  );
345
375
  })
@@ -354,7 +384,7 @@ async function createNotebooksRouter(options) {
354
384
  notebooksRouter.post(
355
385
  "/v1/sessions/:sessionId/query",
356
386
  expensiveRateLimiter,
357
- requireNotebooksPermission,
387
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
358
388
  express__default.default.json({ limit: constant.EXPRESS_JSON_BODY_LIMIT }),
359
389
  withAuth(async (req, res, userId) => {
360
390
  const { sessionId } = req.params;
@@ -1 +1 @@
1
- {"version":3,"file":"notebooksRouters.cjs.js","sources":["../../../src/service/notebooks/notebooksRouters.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HttpAuthService,\n LoggerService,\n PermissionsService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\n\nimport express, { Router } from 'express';\n\nimport { lightspeedNotebooksUsePermission } from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { Readable, Transform } from 'stream';\n\nimport {\n DEFAULT_LIGHTSPEED_SERVICE_HOST,\n DEFAULT_LIGHTSPEED_SERVICE_PORT,\n EXPRESS_JSON_BODY_LIMIT,\n HTTP_STATUS_ACCEPTED,\n HTTP_STATUS_INTERNAL_ERROR,\n MAX_QUERY_LENGTH,\n MAX_QUERY_RETRIES,\n NOTEBOOKS_SYSTEM_PROMPT,\n upload,\n} from '../constant';\nimport { createRateLimitMiddleware } from '../middleware/createRateLimitMiddleware';\nimport {\n createIdentityMiddleware,\n getIdentity,\n} from '../middleware/getIdentity';\nimport { userPermissionAuthorization } from '../permission';\nimport { isValidFileType, parseFileContent } from './documents/documentHelpers';\nimport { DocumentService } from './documents/documentService';\nimport { SessionService } from './sessions/sessionService';\nimport {\n createDocumentListResponse,\n createDocumentResponse,\n createSessionListResponse,\n createSessionResponse,\n} from './types/notebooksTypes';\nimport { handleError } from './utils';\nimport { VectorStoresOperator } from './VectorStoresOperator';\n\nexport interface NotebooksRouterOptions {\n logger: LoggerService;\n config: Config;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n permissions: PermissionsService;\n}\n\nexport async function createNotebooksRouter(\n options: NotebooksRouterOptions,\n): Promise<Router> {\n const { logger, config, httpAuth, userInfo, permissions } = options;\n const notebooksRouter = Router();\n notebooksRouter.use(express.json());\n\n const lightSpeedPort =\n config.getOptionalNumber('intelligent-assistant.servicePort') ??\n DEFAULT_LIGHTSPEED_SERVICE_PORT;\n const lcsBaseUrl = `http://${DEFAULT_LIGHTSPEED_SERVICE_HOST}:${lightSpeedPort}`;\n const queryModel = config.getString(\n 'intelligent-assistant.notebooks.queryDefaults.model',\n );\n const queryProvider = config.getString(\n 'intelligent-assistant.notebooks.queryDefaults.provider_id',\n );\n const systemPrompt = NOTEBOOKS_SYSTEM_PROMPT;\n\n logger.info(`AI Notebooks connecting to Lightspeed-Core at ${lcsBaseUrl}`);\n\n const vectorStoresOperator = VectorStoresOperator.getInstance(\n lcsBaseUrl,\n logger,\n );\n const sessionService = new SessionService(vectorStoresOperator, logger);\n const documentService = new DocumentService(\n vectorStoresOperator,\n logger,\n config,\n );\n\n const authorizer = userPermissionAuthorization(permissions);\n\n const expensiveRateLimiter = createRateLimitMiddleware(\n config,\n 'expensive',\n logger,\n );\n const generalRateLimiter = createRateLimitMiddleware(\n config,\n 'general',\n logger,\n );\n\n const requireNotebooksPermission = async (\n req: any,\n res: any,\n next: any,\n ): Promise<void> => {\n try {\n const { credentials } = getIdentity(req);\n await authorizer.authorizeUser(\n lightspeedNotebooksUsePermission,\n credentials,\n );\n next();\n } catch (error) {\n handleError(logger, res, error, 'Permission denied');\n }\n };\n\n const requireSessionOwnership =\n () => async (req: any, res: any, next: any) => {\n try {\n const { userEntityRef } = getIdentity(req);\n const { sessionId } = req.params;\n await sessionService.readSession(sessionId, userEntityRef);\n next();\n } catch (error) {\n handleError(\n logger,\n res,\n error,\n 'Session ownership verification failed',\n );\n }\n };\n\n const withAuth =\n (handler: (req: any, res: any, userId: string) => Promise<void>) =>\n async (req: any, res: any, next: any) => {\n try {\n const { userEntityRef } = getIdentity(req);\n await handler(req, res, userEntityRef);\n } catch (error) {\n next(error);\n }\n };\n\n /**\n * Transforms Responses API SSE (event:/data: lines) into the legacy\n * streaming format that the frontend useConversationMessages hook expects:\n * data: {\"event\": \"<type>\", \"data\": {...}}\\n\\n\n *\n * Also captures the conversation_id from the first response.created event\n * and persists it on the session when it is new.\n */\n const createResponsesApiTransform = (\n session: any,\n sessionId: string,\n userId: string,\n ) => {\n let buffer = '';\n let conversationCaptured = !!session.metadata?.conversation_id;\n\n return new Transform({\n transform(chunk: any, _encoding: any, callback: any) {\n buffer += chunk.toString();\n\n const blocks = buffer.split('\\n\\n');\n buffer = blocks.pop()!;\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const lines = block.split('\\n');\n let eventType = '';\n let dataLine = '';\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n dataLine = line.slice(6).trim();\n }\n }\n\n if (dataLine === '[DONE]') {\n this.push('data: [DONE]\\n\\n');\n continue;\n }\n\n if (!dataLine) continue;\n\n let parsed: any;\n try {\n parsed = JSON.parse(dataLine);\n } catch {\n continue;\n }\n\n if (eventType === 'response.created') {\n const convId = parsed?.response?.conversation;\n const requestId = parsed?.response?.id;\n\n if (convId && !conversationCaptured) {\n conversationCaptured = true;\n logger.info(`Captured conversation ID: ${convId}`);\n sessionService\n .updateSession(sessionId, userId, undefined, undefined, {\n ...session.metadata,\n conversation_id: convId,\n })\n .catch((err: any) =>\n logger.error(`Failed to update session: ${err}`),\n );\n }\n\n const legacy = {\n event: 'start',\n data: { conversation_id: convId, request_id: requestId },\n };\n this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else if (eventType === 'response.output_text.delta') {\n const legacy = {\n event: 'token',\n data: { token: parsed?.delta ?? '' },\n };\n this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else if (eventType === 'response.completed') {\n // Log the full response to see what we're getting\n logger.info(\n `Full response.completed event: ${JSON.stringify(parsed?.response, null, 2)}`,\n );\n\n // Extract citations/sources from tool calls (file_search results)\n\n // this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else {\n // Log unhandled event types to help identify what we're missing\n logger.debug(`Unhandled SSE event type: ${eventType}`, parsed);\n }\n }\n\n callback();\n },\n\n flush(callback: any) {\n if (buffer.trim()) {\n const lines = buffer.split('\\n');\n let dataLine = '';\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n dataLine = line.slice(6).trim();\n }\n }\n if (dataLine === '[DONE]') {\n this.push('data: [DONE]\\n\\n');\n }\n }\n callback();\n },\n });\n };\n\n notebooksRouter.get('/health', (_req, res) => res.json({ status: 'ok' }));\n notebooksRouter.use(\n '/v1',\n createIdentityMiddleware(httpAuth, userInfo, logger),\n );\n\n notebooksRouter.post(\n '/v1/sessions',\n generalRateLimiter,\n requireNotebooksPermission,\n withAuth(async (req, res, userId) => {\n const { name, description, metadata } = req.body;\n if (!name) {\n handleError(logger, res, 'name is required');\n return;\n }\n const session = await sessionService.createSession(\n userId,\n name,\n description,\n { ...metadata, conversation_id: null },\n );\n res.json(createSessionResponse(session, 'Session created successfully'));\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions',\n generalRateLimiter,\n requireNotebooksPermission,\n withAuth(async (_req, res, userId) => {\n const sessions = await sessionService.listSessions(userId);\n res.json(createSessionListResponse(sessions));\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requireNotebooksPermission,\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const session = await sessionService.readSession(sessionId, userId);\n res.json(\n createSessionResponse(session, 'Session retrieved successfully'),\n );\n }),\n );\n\n notebooksRouter.put(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requireNotebooksPermission,\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { name, description, metadata } = req.body;\n const session = await sessionService.updateSession(\n sessionId,\n userId,\n name,\n description,\n metadata,\n );\n res.json(createSessionResponse(session, 'Session updated successfully'));\n }),\n );\n\n notebooksRouter.delete(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requireNotebooksPermission,\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n await sessionService.deleteSession(sessionId, userId);\n res.json(\n createSessionResponse(\n { session_id: sessionId } as any,\n 'Session deleted successfully',\n ),\n );\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId/documents',\n generalRateLimiter,\n requireNotebooksPermission,\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId } = req.params;\n const fileType = req.query.fileType as string | undefined;\n const documents = await documentService.listDocuments(\n sessionId,\n fileType,\n );\n res.json(createDocumentListResponse(sessionId, documents));\n }),\n );\n\n notebooksRouter.put(\n '/v1/sessions/:sessionId/documents',\n expensiveRateLimiter,\n requireNotebooksPermission,\n upload.single('file') as any,\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { fileType, title, newTitle } = req.body;\n\n if (!title) {\n handleError(logger, res, 'title is required');\n return;\n }\n\n if (!fileType || !isValidFileType(fileType)) {\n handleError(\n logger,\n res,\n `Unsupported file type: ${fileType}. Supported types: md, txt, pdf, json, yaml, yml, log`,\n );\n return;\n }\n\n const session = await sessionService.readSession(sessionId, userId);\n if (!session) {\n handleError(logger, res, 'Session not found');\n return;\n }\n const parsedDocument = await parseFileContent(logger, fileType, req.file);\n const fileId = await documentService.uploadFile(\n parsedDocument.content,\n title,\n );\n\n res.status(HTTP_STATUS_ACCEPTED).json({\n status: 'processing',\n document_id: newTitle || title,\n session_id: sessionId,\n message: 'Document upload started',\n });\n\n // Upload document to vector store in background\n const docName = newTitle || title;\n documentService\n .upsertDocument(sessionId, title, fileType, fileId, newTitle)\n .then(() => logger.info(`Background upload succeeded: ${docName}`))\n .catch((err: any) =>\n logger.error(`Background upload failed: ${docName}`, err),\n );\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId/documents/:documentId/status',\n generalRateLimiter,\n requireNotebooksPermission,\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId, documentId } = req.params;\n const fileStatus = await documentService.getFileStatus(\n sessionId,\n documentId,\n );\n res.json({\n status: fileStatus.status,\n document_id: documentId,\n session_id: sessionId,\n ...(fileStatus.error && { error: fileStatus.error }),\n });\n }),\n );\n\n notebooksRouter.delete(\n '/v1/sessions/:sessionId/documents/:documentId',\n generalRateLimiter,\n requireNotebooksPermission,\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId, documentId } = req.params;\n await documentService.deleteDocument(sessionId, documentId);\n res.json(\n createDocumentResponse(\n documentId,\n sessionId,\n 'Document deleted successfully',\n ),\n );\n }),\n );\n\n const validateNotebooksQuery = (query: unknown): string | null => {\n if (!query) return 'query is required';\n if (typeof query === 'string' && query.length > MAX_QUERY_LENGTH) {\n return `query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`;\n }\n return null;\n };\n\n notebooksRouter.post(\n '/v1/sessions/:sessionId/query',\n expensiveRateLimiter,\n requireNotebooksPermission,\n express.json({ limit: EXPRESS_JSON_BODY_LIMIT }),\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { query } = req.body;\n\n const queryError = validateNotebooksQuery(query);\n if (queryError) {\n handleError(logger, res, queryError);\n return;\n }\n\n const session = await sessionService.readSession(sessionId, userId);\n let conversationId = session.metadata?.conversation_id;\n\n const lcsRequest: any = {\n input: query,\n instructions: systemPrompt,\n tools: [{ type: 'file_search', vector_store_ids: [sessionId] }],\n tool_choice: 'required',\n model: `${queryProvider}/${queryModel}`,\n stream: true,\n temperature: 0.35,\n shield_ids: [],\n max_tool_calls: 15,\n ...(conversationId && { conversation: conversationId }),\n };\n\n for (let retries = 0; retries <= MAX_QUERY_RETRIES; retries++) {\n const abortController = new AbortController();\n\n const response = await fetch(\n `${lcsBaseUrl}/v1/responses?user_id=${encodeURIComponent(userId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(lcsRequest),\n signal: abortController.signal,\n },\n );\n\n // Retry once if conversation not found (orphaned from interrupted query)\n if (\n !response.ok &&\n conversationId &&\n response.status === 404 &&\n retries === 0\n ) {\n logger.warn(`Conversation ${conversationId} not found - retrying`);\n await sessionService.updateSession(\n sessionId,\n userId,\n undefined,\n undefined,\n {\n ...session.metadata,\n conversation_id: null,\n },\n );\n delete lcsRequest.conversation;\n conversationId = null;\n continue;\n }\n\n if (!response.ok) {\n const errorBody = (await response.json()) as any;\n const errorMsg =\n errorBody?.detail?.[0]?.msg ||\n errorBody?.detail?.cause ||\n 'Unknown error';\n const statusCode =\n response.status >= 400 && response.status < 500\n ? response.status\n : HTTP_STATUS_INTERNAL_ERROR;\n res.status(statusCode).json({\n status: 'error',\n error: `Error from Lightspeed Core: ${errorMsg}`,\n });\n return;\n }\n\n if (response.body) {\n const body = Readable.fromWeb(response.body as any);\n const transformStream = createResponsesApiTransform(\n session,\n sessionId,\n userId,\n );\n\n body.on('error', (error: Error) => {\n logger.error(\n `Upstream stream error while processing notebook query: ${error}`,\n );\n if (res.headersSent) {\n res.destroy();\n } else {\n res\n .status(500)\n .json({ status: 'error', error: 'Stream error occurred' });\n }\n abortController.abort();\n transformStream.destroy();\n });\n\n transformStream.on('error', (error: Error) => {\n logger.error(\n `Transform stream error while processing notebook query: ${error}`,\n );\n if (res.headersSent) {\n res.destroy();\n } else {\n res.status(500).json({\n status: 'error',\n error: 'Processing error occurred',\n });\n }\n body.destroy();\n abortController.abort();\n });\n\n res.on('close', () => {\n if (!res.writableFinished) {\n logger.warn(\n 'Client disconnected while processing notebook query',\n );\n abortController.abort();\n body.destroy();\n transformStream.destroy();\n }\n });\n\n body.pipe(transformStream).pipe(res);\n }\n break;\n }\n }),\n );\n\n notebooksRouter.use((err: Error, req: any, res: any, _next: any) =>\n handleError(logger, res, err, req.path),\n );\n\n const router = Router();\n router.use('/notebooks', notebooksRouter);\n return router;\n}\n"],"names":["Router","express","DEFAULT_LIGHTSPEED_SERVICE_PORT","DEFAULT_LIGHTSPEED_SERVICE_HOST","NOTEBOOKS_SYSTEM_PROMPT","VectorStoresOperator","sessionService","SessionService","documentService","DocumentService","userPermissionAuthorization","createRateLimitMiddleware","getIdentity","lightspeedNotebooksUsePermission","handleError","Transform","createIdentityMiddleware","createSessionResponse","createSessionListResponse","createDocumentListResponse","upload","isValidFileType","parseFileContent","HTTP_STATUS_ACCEPTED","createDocumentResponse","MAX_QUERY_LENGTH","EXPRESS_JSON_BODY_LIMIT","MAX_QUERY_RETRIES","HTTP_STATUS_INTERNAL_ERROR","Readable"],"mappings":"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"}
1
+ {"version":3,"file":"notebooksRouters.cjs.js","sources":["../../../src/service/notebooks/notebooksRouters.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n HttpAuthService,\n LoggerService,\n PermissionsService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport type { BasicPermission } from '@backstage/plugin-permission-common';\n\nimport express, { Router } from 'express';\n\nimport {\n iaNotebooksManagePermission,\n iaNotebooksUsePermission,\n} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { Readable, Transform } from 'stream';\n\nimport {\n DEFAULT_LIGHTSPEED_SERVICE_HOST,\n DEFAULT_LIGHTSPEED_SERVICE_PORT,\n EXPRESS_JSON_BODY_LIMIT,\n HTTP_STATUS_ACCEPTED,\n HTTP_STATUS_INTERNAL_ERROR,\n MAX_QUERY_LENGTH,\n MAX_QUERY_RETRIES,\n NOTEBOOKS_SYSTEM_PROMPT,\n upload,\n} from '../constant';\nimport { createRateLimitMiddleware } from '../middleware/createRateLimitMiddleware';\nimport {\n createIdentityMiddleware,\n getIdentity,\n} from '../middleware/getIdentity';\nimport { userPermissionAuthorization } from '../permission';\nimport { isValidFileType, parseFileContent } from './documents/documentHelpers';\nimport { DocumentService } from './documents/documentService';\nimport { SessionService } from './sessions/sessionService';\nimport {\n createDocumentListResponse,\n createDocumentResponse,\n createSessionListResponse,\n createSessionResponse,\n} from './types/notebooksTypes';\nimport { handleError } from './utils';\nimport { VectorStoresOperator } from './VectorStoresOperator';\n\nexport interface NotebooksRouterOptions {\n logger: LoggerService;\n config: Config;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n permissions: PermissionsService;\n}\n\nexport async function createNotebooksRouter(\n options: NotebooksRouterOptions,\n): Promise<Router> {\n const { logger, config, httpAuth, userInfo, permissions } = options;\n const notebooksRouter = Router();\n notebooksRouter.use(express.json());\n\n const lightSpeedPort =\n config.getOptionalNumber('intelligent-assistant.servicePort') ??\n DEFAULT_LIGHTSPEED_SERVICE_PORT;\n const lcsBaseUrl = `http://${DEFAULT_LIGHTSPEED_SERVICE_HOST}:${lightSpeedPort}`;\n const queryModel = config.getString(\n 'intelligent-assistant.notebooks.queryDefaults.model',\n );\n const queryProvider = config.getString(\n 'intelligent-assistant.notebooks.queryDefaults.provider_id',\n );\n const systemPrompt = NOTEBOOKS_SYSTEM_PROMPT;\n\n logger.info(`AI Notebooks connecting to Lightspeed-Core at ${lcsBaseUrl}`);\n\n const vectorStoresOperator = VectorStoresOperator.getInstance(\n lcsBaseUrl,\n logger,\n );\n const sessionService = new SessionService(vectorStoresOperator, logger);\n const documentService = new DocumentService(\n vectorStoresOperator,\n logger,\n config,\n );\n\n const authorizer = userPermissionAuthorization(permissions);\n\n const expensiveRateLimiter = createRateLimitMiddleware(\n config,\n 'expensive',\n logger,\n );\n const generalRateLimiter = createRateLimitMiddleware(\n config,\n 'general',\n logger,\n );\n\n const requirePermission =\n (permission: BasicPermission) =>\n async (req: any, res: any, next: any): Promise<void> => {\n try {\n const { credentials } = getIdentity(req);\n await authorizer.authorizeUser(permission, credentials);\n next();\n } catch (error) {\n handleError(logger, res, error, 'Permission denied');\n }\n };\n\n const requireSessionOwnership =\n () => async (req: any, res: any, next: any) => {\n try {\n const { userEntityRef } = getIdentity(req);\n const { sessionId } = req.params;\n await sessionService.readSession(sessionId, userEntityRef);\n next();\n } catch (error) {\n handleError(\n logger,\n res,\n error,\n 'Session ownership verification failed',\n );\n }\n };\n\n const withAuth =\n (handler: (req: any, res: any, userId: string) => Promise<void>) =>\n async (req: any, res: any, next: any) => {\n try {\n const { userEntityRef } = getIdentity(req);\n await handler(req, res, userEntityRef);\n } catch (error) {\n next(error);\n }\n };\n\n /**\n * Transforms Responses API SSE (event:/data: lines) into the legacy\n * streaming format that the frontend useConversationMessages hook expects:\n * data: {\"event\": \"<type>\", \"data\": {...}}\\n\\n\n *\n * Also captures the conversation_id from the first response.created event\n * and persists it on the session when it is new.\n */\n const createResponsesApiTransform = (\n session: any,\n sessionId: string,\n userId: string,\n ) => {\n let buffer = '';\n let conversationCaptured = !!session.metadata?.conversation_id;\n\n return new Transform({\n transform(chunk: any, _encoding: any, callback: any) {\n buffer += chunk.toString();\n\n const blocks = buffer.split('\\n\\n');\n buffer = blocks.pop()!;\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const lines = block.split('\\n');\n let eventType = '';\n let dataLine = '';\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n dataLine = line.slice(6).trim();\n }\n }\n\n if (dataLine === '[DONE]') {\n this.push('data: [DONE]\\n\\n');\n continue;\n }\n\n if (!dataLine) continue;\n\n let parsed: any;\n try {\n parsed = JSON.parse(dataLine);\n } catch {\n continue;\n }\n\n if (eventType === 'response.created') {\n const convId = parsed?.response?.conversation;\n const requestId = parsed?.response?.id;\n\n if (convId && !conversationCaptured) {\n conversationCaptured = true;\n logger.info(`Captured conversation ID: ${convId}`);\n sessionService\n .updateSession(sessionId, userId, undefined, undefined, {\n ...session.metadata,\n conversation_id: convId,\n })\n .catch((err: any) =>\n logger.error(`Failed to update session: ${err}`),\n );\n }\n\n const legacy = {\n event: 'start',\n data: { conversation_id: convId, request_id: requestId },\n };\n this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else if (eventType === 'response.output_text.delta') {\n const legacy = {\n event: 'token',\n data: { token: parsed?.delta ?? '' },\n };\n this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else if (eventType === 'response.completed') {\n // Log the full response to see what we're getting\n logger.info(\n `Full response.completed event: ${JSON.stringify(parsed?.response, null, 2)}`,\n );\n\n // Extract citations/sources from tool calls (file_search results)\n\n // this.push(`data: ${JSON.stringify(legacy)}\\n\\n`);\n } else {\n // Log unhandled event types to help identify what we're missing\n logger.debug(`Unhandled SSE event type: ${eventType}`, parsed);\n }\n }\n\n callback();\n },\n\n flush(callback: any) {\n if (buffer.trim()) {\n const lines = buffer.split('\\n');\n let dataLine = '';\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n dataLine = line.slice(6).trim();\n }\n }\n if (dataLine === '[DONE]') {\n this.push('data: [DONE]\\n\\n');\n }\n }\n callback();\n },\n });\n };\n\n notebooksRouter.get('/health', (_req, res) => res.json({ status: 'ok' }));\n notebooksRouter.use(\n '/v1',\n createIdentityMiddleware(httpAuth, userInfo, logger),\n );\n\n notebooksRouter.post(\n '/v1/sessions',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n withAuth(async (req, res, userId) => {\n const { name, description, metadata } = req.body;\n if (!name) {\n handleError(logger, res, 'name is required');\n return;\n }\n const session = await sessionService.createSession(\n userId,\n name,\n description,\n { ...metadata, conversation_id: null },\n );\n res.json(createSessionResponse(session, 'Session created successfully'));\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n withAuth(async (_req, res, userId) => {\n const sessions = await sessionService.listSessions(userId);\n res.json(createSessionListResponse(sessions));\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const session = await sessionService.readSession(sessionId, userId);\n res.json(\n createSessionResponse(session, 'Session retrieved successfully'),\n );\n }),\n );\n\n notebooksRouter.put(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requirePermission(iaNotebooksManagePermission),\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { name, description, metadata } = req.body;\n const session = await sessionService.updateSession(\n sessionId,\n userId,\n name,\n description,\n metadata,\n );\n res.json(createSessionResponse(session, 'Session updated successfully'));\n }),\n );\n\n notebooksRouter.delete(\n '/v1/sessions/:sessionId',\n generalRateLimiter,\n requirePermission(iaNotebooksManagePermission),\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n await sessionService.deleteSession(sessionId, userId);\n res.json(\n createSessionResponse(\n { session_id: sessionId } as any,\n 'Session deleted successfully',\n ),\n );\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId/documents',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId } = req.params;\n const fileType = req.query.fileType as string | undefined;\n const documents = await documentService.listDocuments(\n sessionId,\n fileType,\n );\n res.json(createDocumentListResponse(sessionId, documents));\n }),\n );\n\n notebooksRouter.put(\n '/v1/sessions/:sessionId/documents',\n expensiveRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n upload.single('file') as any,\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { fileType, title, newTitle } = req.body;\n\n if (!title) {\n handleError(logger, res, 'title is required');\n return;\n }\n\n if (!fileType || !isValidFileType(fileType)) {\n handleError(\n logger,\n res,\n `Unsupported file type: ${fileType}. Supported types: md, txt, pdf, json, yaml, yml, log`,\n );\n return;\n }\n\n const session = await sessionService.readSession(sessionId, userId);\n if (!session) {\n handleError(logger, res, 'Session not found');\n return;\n }\n const parsedDocument = await parseFileContent(logger, fileType, req.file);\n const fileId = await documentService.uploadFile(\n parsedDocument.content,\n title,\n );\n\n res.status(HTTP_STATUS_ACCEPTED).json({\n status: 'processing',\n document_id: newTitle || title,\n session_id: sessionId,\n message: 'Resource upload started',\n });\n\n // Upload document to vector store in background\n const docName = newTitle || title;\n documentService\n .upsertDocument(sessionId, title, { fileType, fileId, newTitle })\n .then(() => logger.info(`Background upload succeeded: ${docName}`))\n .catch((err: any) =>\n logger.error(`Background upload failed: ${docName}`, err),\n );\n }),\n );\n\n notebooksRouter.get(\n '/v1/sessions/:sessionId/documents/:documentId/status',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId, documentId } = req.params;\n const fileStatus = await documentService.getFileStatus(\n sessionId,\n documentId,\n );\n res.json({\n status: fileStatus.status,\n document_id: documentId,\n session_id: sessionId,\n ...(fileStatus.error && { error: fileStatus.error }),\n });\n }),\n );\n\n notebooksRouter.patch(\n '/v1/sessions/:sessionId/documents/:documentId',\n generalRateLimiter,\n requirePermission(iaNotebooksManagePermission),\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId, documentId } = req.params;\n const { title } = req.body;\n\n if (!title || typeof title !== 'string' || !title.trim()) {\n handleError(logger, res, 'title is required');\n return;\n }\n\n const MAX_TITLE_LENGTH = 255;\n if (title.trim().length > MAX_TITLE_LENGTH) {\n handleError(\n logger,\n res,\n `title must be ${MAX_TITLE_LENGTH} characters or less`,\n );\n return;\n }\n\n await documentService.upsertDocument(sessionId, documentId, {\n newTitle: title.trim(),\n });\n res.json(\n createDocumentResponse(\n title.trim(),\n sessionId,\n 'Document renamed successfully',\n ),\n );\n }),\n );\n\n notebooksRouter.delete(\n '/v1/sessions/:sessionId/documents/:documentId',\n generalRateLimiter,\n requirePermission(iaNotebooksManagePermission),\n requireSessionOwnership(),\n withAuth(async (req, res) => {\n const { sessionId, documentId } = req.params;\n await documentService.deleteDocument(sessionId, documentId);\n res.json(\n createDocumentResponse(\n documentId,\n sessionId,\n 'Resource deleted successfully',\n ),\n );\n }),\n );\n\n const validateNotebooksQuery = (query: unknown): string | null => {\n if (!query) return 'query is required';\n if (typeof query === 'string' && query.length > MAX_QUERY_LENGTH) {\n return `query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`;\n }\n return null;\n };\n\n notebooksRouter.post(\n '/v1/sessions/:sessionId/query',\n expensiveRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n express.json({ limit: EXPRESS_JSON_BODY_LIMIT }),\n withAuth(async (req, res, userId) => {\n const { sessionId } = req.params;\n const { query } = req.body;\n\n const queryError = validateNotebooksQuery(query);\n if (queryError) {\n handleError(logger, res, queryError);\n return;\n }\n\n const session = await sessionService.readSession(sessionId, userId);\n let conversationId = session.metadata?.conversation_id;\n\n const lcsRequest: any = {\n input: query,\n instructions: systemPrompt,\n tools: [{ type: 'file_search', vector_store_ids: [sessionId] }],\n tool_choice: 'required',\n model: `${queryProvider}/${queryModel}`,\n stream: true,\n temperature: 0.35,\n shield_ids: [],\n max_tool_calls: 15,\n ...(conversationId && { conversation: conversationId }),\n };\n\n for (let retries = 0; retries <= MAX_QUERY_RETRIES; retries++) {\n const abortController = new AbortController();\n\n const response = await fetch(\n `${lcsBaseUrl}/v1/responses?user_id=${encodeURIComponent(userId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(lcsRequest),\n signal: abortController.signal,\n },\n );\n\n // Retry once if conversation not found (orphaned from interrupted query)\n if (\n !response.ok &&\n conversationId &&\n response.status === 404 &&\n retries === 0\n ) {\n logger.warn(`Conversation ${conversationId} not found - retrying`);\n await sessionService.updateSession(\n sessionId,\n userId,\n undefined,\n undefined,\n {\n ...session.metadata,\n conversation_id: null,\n },\n );\n delete lcsRequest.conversation;\n conversationId = null;\n continue;\n }\n\n if (!response.ok) {\n const errorBody = (await response.json()) as any;\n const errorMsg =\n errorBody?.detail?.[0]?.msg ||\n errorBody?.detail?.cause ||\n 'Unknown error';\n const statusCode =\n response.status >= 400 && response.status < 500\n ? response.status\n : HTTP_STATUS_INTERNAL_ERROR;\n res.status(statusCode).json({\n status: 'error',\n error: `Error from Lightspeed Core: ${errorMsg}`,\n });\n return;\n }\n\n if (response.body) {\n const body = Readable.fromWeb(response.body as any);\n const transformStream = createResponsesApiTransform(\n session,\n sessionId,\n userId,\n );\n\n body.on('error', (error: Error) => {\n logger.error(\n `Upstream stream error while processing notebook query: ${error}`,\n );\n if (res.headersSent) {\n res.destroy();\n } else {\n res\n .status(500)\n .json({ status: 'error', error: 'Stream error occurred' });\n }\n abortController.abort();\n transformStream.destroy();\n });\n\n transformStream.on('error', (error: Error) => {\n logger.error(\n `Transform stream error while processing notebook query: ${error}`,\n );\n if (res.headersSent) {\n res.destroy();\n } else {\n res.status(500).json({\n status: 'error',\n error: 'Processing error occurred',\n });\n }\n body.destroy();\n abortController.abort();\n });\n\n res.on('close', () => {\n if (!res.writableFinished) {\n logger.warn(\n 'Client disconnected while processing notebook query',\n );\n abortController.abort();\n body.destroy();\n transformStream.destroy();\n }\n });\n\n body.pipe(transformStream).pipe(res);\n }\n break;\n }\n }),\n );\n\n notebooksRouter.use((err: Error, req: any, res: any, _next: any) =>\n handleError(logger, res, err, req.path),\n );\n\n const router = Router();\n router.use('/notebooks', notebooksRouter);\n return router;\n}\n"],"names":["Router","express","DEFAULT_LIGHTSPEED_SERVICE_PORT","DEFAULT_LIGHTSPEED_SERVICE_HOST","NOTEBOOKS_SYSTEM_PROMPT","VectorStoresOperator","sessionService","SessionService","documentService","DocumentService","userPermissionAuthorization","createRateLimitMiddleware","getIdentity","handleError","Transform","createIdentityMiddleware","iaNotebooksUsePermission","createSessionResponse","createSessionListResponse","iaNotebooksManagePermission","createDocumentListResponse","upload","isValidFileType","parseFileContent","HTTP_STATUS_ACCEPTED","createDocumentResponse","MAX_QUERY_LENGTH","EXPRESS_JSON_BODY_LIMIT","MAX_QUERY_RETRIES","HTTP_STATUS_INTERNAL_ERROR","Readable"],"mappings":"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"}
@@ -7,6 +7,7 @@ var express = require('express');
7
7
  var httpProxyMiddleware = require('http-proxy-middleware');
8
8
  var backstagePluginIntelligentAssistantCommon = require('@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common');
9
9
  var node_stream = require('node:stream');
10
+ var attachmentValidation = require('./attachment-validation.cjs.js');
10
11
  var constant = require('./constant.cjs.js');
11
12
  var mcpServerStore = require('./mcp-server-store.cjs.js');
12
13
  var mcpServerValidator = require('./mcp-server-validator.cjs.js');
@@ -137,7 +138,7 @@ async function createRouter(options) {
137
138
  response.json({ status: "ok" });
138
139
  });
139
140
  const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
140
- permissions: backstagePluginIntelligentAssistantCommon.lightspeedPermissions
141
+ permissions: backstagePluginIntelligentAssistantCommon.iaPermissions
141
142
  });
142
143
  router.use(permissionIntegrationRouter);
143
144
  const identityMiddleware = getIdentity.createIdentityMiddleware(
@@ -168,7 +169,7 @@ async function createRouter(options) {
168
169
  router.get(
169
170
  "/mcp-servers",
170
171
  generalRateLimiter,
171
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedMcpReadPermission),
172
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaMcpUsePermission),
172
173
  async (req, res) => {
173
174
  try {
174
175
  const { userEntityRef } = getIdentity.getIdentity(req);
@@ -203,7 +204,7 @@ async function createRouter(options) {
203
204
  router.post(
204
205
  "/mcp-servers/validate",
205
206
  generalRateLimiter,
206
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedMcpReadPermission),
207
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaMcpUsePermission),
207
208
  async (req, res) => {
208
209
  try {
209
210
  const { url, token } = req.body;
@@ -233,7 +234,7 @@ async function createRouter(options) {
233
234
  router.post(
234
235
  "/mcp-servers/:name/validate",
235
236
  generalRateLimiter,
236
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedMcpManagePermission),
237
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaMcpManagePermission),
237
238
  async (req, res) => {
238
239
  try {
239
240
  const { userEntityRef, credentials } = getIdentity.getIdentity(req);
@@ -305,7 +306,7 @@ async function createRouter(options) {
305
306
  router.patch(
306
307
  "/mcp-servers/:name",
307
308
  generalRateLimiter,
308
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedMcpManagePermission),
309
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaMcpManagePermission),
309
310
  async (req, res) => {
310
311
  try {
311
312
  const { userEntityRef } = getIdentity.getIdentity(req);
@@ -379,6 +380,7 @@ async function createRouter(options) {
379
380
  router.get(
380
381
  "/notebook-conversation-ids",
381
382
  generalRateLimiter,
383
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaNotebooksUsePermission),
382
384
  async (req, res) => {
383
385
  try {
384
386
  const { userEntityRef } = getIdentity.getIdentity(req);
@@ -409,43 +411,68 @@ async function createRouter(options) {
409
411
  router.get(
410
412
  "/v1/models",
411
413
  generalRateLimiter,
412
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
414
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatAccessPermission),
413
415
  apiProxy
414
416
  );
415
417
  router.get(
416
418
  "/v1/shields",
417
419
  generalRateLimiter,
418
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
420
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatAccessPermission),
419
421
  apiProxy
420
422
  );
421
423
  router.get(
422
424
  "/v2/conversations",
423
425
  generalRateLimiter,
424
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
426
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatAccessPermission),
425
427
  apiProxy
426
428
  );
427
429
  router.get(
428
430
  "/v2/conversations/:conversation_id",
429
431
  generalRateLimiter,
430
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
432
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatAccessPermission),
431
433
  apiProxy
432
434
  );
433
435
  router.delete(
434
436
  "/v2/conversations/:conversation_id",
435
437
  generalRateLimiter,
436
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatDeletePermission),
438
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatManagePermission),
437
439
  apiProxy
438
440
  );
439
441
  router.get(
440
442
  "/v1/feedback/status",
441
443
  generalRateLimiter,
442
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
444
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatAccessPermission),
445
+ apiProxy
446
+ );
447
+ router.get(
448
+ "/v1/saved-prompts/config",
449
+ generalRateLimiter,
450
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
451
+ apiProxy
452
+ // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint
453
+ );
454
+ router.get(
455
+ "/v1/saved-prompts",
456
+ generalRateLimiter,
457
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
458
+ apiProxy
459
+ );
460
+ router.delete(
461
+ "/v1/saved-prompts/:prompt_id",
462
+ generalRateLimiter,
463
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
464
+ apiProxy
465
+ );
466
+ router.get(
467
+ "/v1/skills",
468
+ generalRateLimiter,
469
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaSkillsAccessPermission),
443
470
  apiProxy
444
471
  );
445
472
  router.post(
446
473
  "/v1/feedback",
447
474
  generalRateLimiter,
448
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatCreatePermission),
475
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
449
476
  async (request, response) => {
450
477
  try {
451
478
  const { userEntityRef } = getIdentity.getIdentity(request);
@@ -480,10 +507,50 @@ async function createRouter(options) {
480
507
  }
481
508
  }
482
509
  );
510
+ router.post(
511
+ "/v1/saved-prompts",
512
+ generalRateLimiter,
513
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
514
+ async (request, response) => {
515
+ try {
516
+ const { userEntityRef } = getIdentity.getIdentity(request);
517
+ logger.info(
518
+ `/v1/saved-prompts receives call from user: ${userEntityRef}`
519
+ );
520
+ const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;
521
+ const requestBody = JSON.stringify(request.body);
522
+ const fetchResponse = await fetch(
523
+ `${lcsBaseUrl}/v1/saved-prompts?${userQueryParam}`,
524
+ {
525
+ method: "POST",
526
+ headers: {
527
+ "Content-Type": "application/json"
528
+ },
529
+ body: requestBody
530
+ }
531
+ );
532
+ if (!fetchResponse.ok) {
533
+ await utils.handleLCSFetchError(
534
+ fetchResponse,
535
+ logger,
536
+ "creating saved prompt",
537
+ response
538
+ );
539
+ return;
540
+ }
541
+ const data = await fetchResponse.json();
542
+ response.status(fetchResponse.status).json(data);
543
+ } catch (error) {
544
+ const errormsg = `Error while creating saved prompt: ${error}`;
545
+ logger.error(errormsg);
546
+ response.status(500).json({ error: errormsg });
547
+ }
548
+ }
549
+ );
483
550
  router.post(
484
551
  "/v1/query/interrupt",
485
552
  generalRateLimiter,
486
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatCreatePermission),
553
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
487
554
  async (request, response) => {
488
555
  try {
489
556
  const { userEntityRef } = getIdentity.getIdentity(request);
@@ -521,12 +588,18 @@ async function createRouter(options) {
521
588
  express__default.default.json({ limit: constant.EXPRESS_JSON_BODY_LIMIT }),
522
589
  expensiveRateLimiter,
523
590
  validation.validateCompletionsRequest,
524
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatCreatePermission),
591
+ validation.validateAttachmentsForModel,
592
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
525
593
  async (request, response) => {
526
594
  const { provider } = request.body;
527
595
  try {
528
596
  const { userEntityRef, credentials } = getIdentity.getIdentity(request);
529
597
  logger.info(`/v1/query receives call from user: ${userEntityRef}`);
598
+ if (request.body.attachments?.length) {
599
+ logger.info(
600
+ `/v1/query includes ${request.body.attachments.length} attachment(s): ${request.body.attachments.map((a) => a.attachment_type).join(", ")}`
601
+ );
602
+ }
530
603
  const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;
531
604
  request.body.media_type = "application/json";
532
605
  if (system_prompt && system_prompt.trim().length > 0) {
@@ -593,7 +666,7 @@ async function createRouter(options) {
593
666
  router.put(
594
667
  "/v2/conversations/:conversation_id",
595
668
  generalRateLimiter,
596
- requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatCreatePermission),
669
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatManagePermission),
597
670
  async (request, response) => {
598
671
  try {
599
672
  const { userEntityRef } = getIdentity.getIdentity(request);
@@ -628,6 +701,68 @@ async function createRouter(options) {
628
701
  }
629
702
  }
630
703
  );
704
+ router.post(
705
+ "/v1/validate-model-vision",
706
+ generalRateLimiter,
707
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
708
+ async (request, response) => {
709
+ const { model, provider } = request.body;
710
+ if (typeof model !== "string" || !model.trim() || typeof provider !== "string" || !provider.trim()) {
711
+ response.status(400).json({ error: "model and provider are required" });
712
+ return;
713
+ }
714
+ const cacheKey = `${provider}/${model}`;
715
+ try {
716
+ logger.info(`Vision validation requested for model: ${cacheKey}`);
717
+ if (attachmentValidation.ModelCapabilitiesCache.has(cacheKey)) {
718
+ const cached = attachmentValidation.ModelCapabilitiesCache.get(cacheKey);
719
+ logger.info(`Cache hit for ${cacheKey}: ${cached}`);
720
+ response.json({ model, provider, supportsVision: cached });
721
+ return;
722
+ }
723
+ const testJpeg = `data:image/jpeg;base64,${constant.TEST_VISION_JPEG}`;
724
+ const testResponse = await fetch(`${lcsBaseUrl}/v1/responses`, {
725
+ method: "POST",
726
+ headers: { "Content-Type": "application/json" },
727
+ body: JSON.stringify({
728
+ model: cacheKey,
729
+ input: [
730
+ {
731
+ type: "message",
732
+ role: "user",
733
+ content: [
734
+ {
735
+ type: "input_image",
736
+ image_url: testJpeg,
737
+ detail: "low"
738
+ },
739
+ {
740
+ type: "input_text",
741
+ text: "hi, respond with hi."
742
+ }
743
+ ]
744
+ }
745
+ ],
746
+ tool_choice: "none",
747
+ temperature: 0,
748
+ store: false,
749
+ stream: false
750
+ })
751
+ });
752
+ if (testResponse.ok) {
753
+ attachmentValidation.ModelCapabilitiesCache.set(cacheKey, true);
754
+ response.json({ model, provider, supportsVision: true });
755
+ }
756
+ } catch (error) {
757
+ logger.error(`Vision test error for ${cacheKey}:`, error);
758
+ response.status(502).json({
759
+ error: "Unable to verify vision support for model \u2014 upstream unreachable",
760
+ model,
761
+ provider
762
+ });
763
+ }
764
+ }
765
+ );
631
766
  router.use((_request, response) => {
632
767
  response.status(404).json({ error: "Requested path is not available" });
633
768
  });