@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend 3.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +607 -0
  2. package/README.md +400 -0
  3. package/app-config.yaml +25 -0
  4. package/config.d.ts +142 -0
  5. package/dist/database/migration.cjs.js +19 -0
  6. package/dist/database/migration.cjs.js.map +1 -0
  7. package/dist/index.cjs.js +12 -0
  8. package/dist/index.cjs.js.map +1 -0
  9. package/dist/index.d.ts +73 -0
  10. package/dist/plugin.cjs.js +95 -0
  11. package/dist/plugin.cjs.js.map +1 -0
  12. package/dist/service/constant.cjs.js +107 -0
  13. package/dist/service/constant.cjs.js.map +1 -0
  14. package/dist/service/mcp-server-store.cjs.js +107 -0
  15. package/dist/service/mcp-server-store.cjs.js.map +1 -0
  16. package/dist/service/mcp-server-validator.cjs.js +215 -0
  17. package/dist/service/mcp-server-validator.cjs.js.map +1 -0
  18. package/dist/service/middleware/createPermissionMiddleware.cjs.js +24 -0
  19. package/dist/service/middleware/createPermissionMiddleware.cjs.js.map +1 -0
  20. package/dist/service/middleware/createRateLimitMiddleware.cjs.js +52 -0
  21. package/dist/service/middleware/createRateLimitMiddleware.cjs.js.map +1 -0
  22. package/dist/service/middleware/getIdentity.cjs.js +39 -0
  23. package/dist/service/middleware/getIdentity.cjs.js.map +1 -0
  24. package/dist/service/notebooks/VectorStoresOperator.cjs.js +347 -0
  25. package/dist/service/notebooks/VectorStoresOperator.cjs.js.map +1 -0
  26. package/dist/service/notebooks/documents/documentHelpers.cjs.js +36 -0
  27. package/dist/service/notebooks/documents/documentHelpers.cjs.js.map +1 -0
  28. package/dist/service/notebooks/documents/documentService.cjs.js +189 -0
  29. package/dist/service/notebooks/documents/documentService.cjs.js.map +1 -0
  30. package/dist/service/notebooks/documents/fileParser.cjs.js +139 -0
  31. package/dist/service/notebooks/documents/fileParser.cjs.js.map +1 -0
  32. package/dist/service/notebooks/notebooksRouters.cjs.js +477 -0
  33. package/dist/service/notebooks/notebooksRouters.cjs.js.map +1 -0
  34. package/dist/service/notebooks/sessions/sessionService.cjs.js +226 -0
  35. package/dist/service/notebooks/sessions/sessionService.cjs.js.map +1 -0
  36. package/dist/service/notebooks/types/notebooksTypes.cjs.js +40 -0
  37. package/dist/service/notebooks/types/notebooksTypes.cjs.js.map +1 -0
  38. package/dist/service/notebooks/utils.cjs.js +70 -0
  39. package/dist/service/notebooks/utils.cjs.js.map +1 -0
  40. package/dist/service/permission.cjs.js +26 -0
  41. package/dist/service/permission.cjs.js.map +1 -0
  42. package/dist/service/router.cjs.js +638 -0
  43. package/dist/service/router.cjs.js.map +1 -0
  44. package/dist/service/token-encryption.cjs.js +101 -0
  45. package/dist/service/token-encryption.cjs.js.map +1 -0
  46. package/dist/service/utils.cjs.js +40 -0
  47. package/dist/service/utils.cjs.js.map +1 -0
  48. package/dist/service/validation.cjs.js +45 -0
  49. package/dist/service/validation.cjs.js.map +1 -0
  50. package/migrations/20260302120000_add_mcp_servers.js +41 -0
  51. package/package.json +109 -0
@@ -0,0 +1,477 @@
1
+ 'use strict';
2
+
3
+ var express = require('express');
4
+ var backstagePluginIntelligentAssistantCommon = require('@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common');
5
+ var stream = require('stream');
6
+ var constant = require('../constant.cjs.js');
7
+ var createRateLimitMiddleware = require('../middleware/createRateLimitMiddleware.cjs.js');
8
+ var getIdentity = require('../middleware/getIdentity.cjs.js');
9
+ var permission = require('../permission.cjs.js');
10
+ var documentHelpers = require('./documents/documentHelpers.cjs.js');
11
+ var documentService = require('./documents/documentService.cjs.js');
12
+ var sessionService = require('./sessions/sessionService.cjs.js');
13
+ var notebooksTypes = require('./types/notebooksTypes.cjs.js');
14
+ var utils = require('./utils.cjs.js');
15
+ var VectorStoresOperator = require('./VectorStoresOperator.cjs.js');
16
+
17
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
18
+
19
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
20
+
21
+ async function createNotebooksRouter(options) {
22
+ const { logger, config, httpAuth, userInfo, permissions } = options;
23
+ const notebooksRouter = express.Router();
24
+ notebooksRouter.use(express__default.default.json());
25
+ const lightSpeedPort = config.getOptionalNumber("intelligent-assistant.servicePort") ?? constant.DEFAULT_LIGHTSPEED_SERVICE_PORT;
26
+ const lcsBaseUrl = `http://${constant.DEFAULT_LIGHTSPEED_SERVICE_HOST}:${lightSpeedPort}`;
27
+ const queryModel = config.getString(
28
+ "intelligent-assistant.notebooks.queryDefaults.model"
29
+ );
30
+ const queryProvider = config.getString(
31
+ "intelligent-assistant.notebooks.queryDefaults.provider_id"
32
+ );
33
+ const systemPrompt = constant.NOTEBOOKS_SYSTEM_PROMPT;
34
+ logger.info(`AI Notebooks connecting to Lightspeed-Core at ${lcsBaseUrl}`);
35
+ const vectorStoresOperator = VectorStoresOperator.VectorStoresOperator.getInstance(
36
+ lcsBaseUrl,
37
+ logger
38
+ );
39
+ const sessionService$1 = new sessionService.SessionService(vectorStoresOperator, logger);
40
+ const documentService$1 = new documentService.DocumentService(
41
+ vectorStoresOperator,
42
+ logger,
43
+ config
44
+ );
45
+ const authorizer = permission.userPermissionAuthorization(permissions);
46
+ const expensiveRateLimiter = createRateLimitMiddleware.createRateLimitMiddleware(
47
+ config,
48
+ "expensive",
49
+ logger
50
+ );
51
+ const generalRateLimiter = createRateLimitMiddleware.createRateLimitMiddleware(
52
+ config,
53
+ "general",
54
+ logger
55
+ );
56
+ const requireNotebooksPermission = async (req, res, next) => {
57
+ try {
58
+ const { credentials } = getIdentity.getIdentity(req);
59
+ await authorizer.authorizeUser(
60
+ backstagePluginIntelligentAssistantCommon.lightspeedNotebooksUsePermission,
61
+ credentials
62
+ );
63
+ next();
64
+ } catch (error) {
65
+ utils.handleError(logger, res, error, "Permission denied");
66
+ }
67
+ };
68
+ const requireSessionOwnership = () => async (req, res, next) => {
69
+ try {
70
+ const { userEntityRef } = getIdentity.getIdentity(req);
71
+ const { sessionId } = req.params;
72
+ await sessionService$1.readSession(sessionId, userEntityRef);
73
+ next();
74
+ } catch (error) {
75
+ utils.handleError(
76
+ logger,
77
+ res,
78
+ error,
79
+ "Session ownership verification failed"
80
+ );
81
+ }
82
+ };
83
+ const withAuth = (handler) => async (req, res, next) => {
84
+ try {
85
+ const { userEntityRef } = getIdentity.getIdentity(req);
86
+ await handler(req, res, userEntityRef);
87
+ } catch (error) {
88
+ next(error);
89
+ }
90
+ };
91
+ const createResponsesApiTransform = (session, sessionId, userId) => {
92
+ let buffer = "";
93
+ let conversationCaptured = !!session.metadata?.conversation_id;
94
+ return new stream.Transform({
95
+ transform(chunk, _encoding, callback) {
96
+ buffer += chunk.toString();
97
+ const blocks = buffer.split("\n\n");
98
+ buffer = blocks.pop();
99
+ for (const block of blocks) {
100
+ if (!block.trim()) continue;
101
+ const lines = block.split("\n");
102
+ let eventType = "";
103
+ let dataLine = "";
104
+ for (const line of lines) {
105
+ if (line.startsWith("event: ")) {
106
+ eventType = line.slice(7).trim();
107
+ } else if (line.startsWith("data: ")) {
108
+ dataLine = line.slice(6).trim();
109
+ }
110
+ }
111
+ if (dataLine === "[DONE]") {
112
+ this.push("data: [DONE]\n\n");
113
+ continue;
114
+ }
115
+ if (!dataLine) continue;
116
+ let parsed;
117
+ try {
118
+ parsed = JSON.parse(dataLine);
119
+ } catch {
120
+ continue;
121
+ }
122
+ if (eventType === "response.created") {
123
+ const convId = parsed?.response?.conversation;
124
+ const requestId = parsed?.response?.id;
125
+ if (convId && !conversationCaptured) {
126
+ conversationCaptured = true;
127
+ logger.info(`Captured conversation ID: ${convId}`);
128
+ sessionService$1.updateSession(sessionId, userId, void 0, void 0, {
129
+ ...session.metadata,
130
+ conversation_id: convId
131
+ }).catch(
132
+ (err) => logger.error(`Failed to update session: ${err}`)
133
+ );
134
+ }
135
+ const legacy = {
136
+ event: "start",
137
+ data: { conversation_id: convId, request_id: requestId }
138
+ };
139
+ this.push(`data: ${JSON.stringify(legacy)}
140
+
141
+ `);
142
+ } else if (eventType === "response.output_text.delta") {
143
+ const legacy = {
144
+ event: "token",
145
+ data: { token: parsed?.delta ?? "" }
146
+ };
147
+ this.push(`data: ${JSON.stringify(legacy)}
148
+
149
+ `);
150
+ } else if (eventType === "response.completed") {
151
+ logger.info(
152
+ `Full response.completed event: ${JSON.stringify(parsed?.response, null, 2)}`
153
+ );
154
+ } else {
155
+ logger.debug(`Unhandled SSE event type: ${eventType}`, parsed);
156
+ }
157
+ }
158
+ callback();
159
+ },
160
+ flush(callback) {
161
+ if (buffer.trim()) {
162
+ const lines = buffer.split("\n");
163
+ let dataLine = "";
164
+ for (const line of lines) {
165
+ if (line.startsWith("data: ")) {
166
+ dataLine = line.slice(6).trim();
167
+ }
168
+ }
169
+ if (dataLine === "[DONE]") {
170
+ this.push("data: [DONE]\n\n");
171
+ }
172
+ }
173
+ callback();
174
+ }
175
+ });
176
+ };
177
+ notebooksRouter.get("/health", (_req, res) => res.json({ status: "ok" }));
178
+ notebooksRouter.use(
179
+ "/v1",
180
+ getIdentity.createIdentityMiddleware(httpAuth, userInfo, logger)
181
+ );
182
+ notebooksRouter.post(
183
+ "/v1/sessions",
184
+ generalRateLimiter,
185
+ requireNotebooksPermission,
186
+ withAuth(async (req, res, userId) => {
187
+ const { name, description, metadata } = req.body;
188
+ if (!name) {
189
+ utils.handleError(logger, res, "name is required");
190
+ return;
191
+ }
192
+ const session = await sessionService$1.createSession(
193
+ userId,
194
+ name,
195
+ description,
196
+ { ...metadata, conversation_id: null }
197
+ );
198
+ res.json(notebooksTypes.createSessionResponse(session, "Session created successfully"));
199
+ })
200
+ );
201
+ notebooksRouter.get(
202
+ "/v1/sessions",
203
+ generalRateLimiter,
204
+ requireNotebooksPermission,
205
+ withAuth(async (_req, res, userId) => {
206
+ const sessions = await sessionService$1.listSessions(userId);
207
+ res.json(notebooksTypes.createSessionListResponse(sessions));
208
+ })
209
+ );
210
+ notebooksRouter.get(
211
+ "/v1/sessions/:sessionId",
212
+ generalRateLimiter,
213
+ requireNotebooksPermission,
214
+ withAuth(async (req, res, userId) => {
215
+ const { sessionId } = req.params;
216
+ const session = await sessionService$1.readSession(sessionId, userId);
217
+ res.json(
218
+ notebooksTypes.createSessionResponse(session, "Session retrieved successfully")
219
+ );
220
+ })
221
+ );
222
+ notebooksRouter.put(
223
+ "/v1/sessions/:sessionId",
224
+ generalRateLimiter,
225
+ requireNotebooksPermission,
226
+ withAuth(async (req, res, userId) => {
227
+ const { sessionId } = req.params;
228
+ const { name, description, metadata } = req.body;
229
+ const session = await sessionService$1.updateSession(
230
+ sessionId,
231
+ userId,
232
+ name,
233
+ description,
234
+ metadata
235
+ );
236
+ res.json(notebooksTypes.createSessionResponse(session, "Session updated successfully"));
237
+ })
238
+ );
239
+ notebooksRouter.delete(
240
+ "/v1/sessions/:sessionId",
241
+ generalRateLimiter,
242
+ requireNotebooksPermission,
243
+ withAuth(async (req, res, userId) => {
244
+ const { sessionId } = req.params;
245
+ await sessionService$1.deleteSession(sessionId, userId);
246
+ res.json(
247
+ notebooksTypes.createSessionResponse(
248
+ { session_id: sessionId },
249
+ "Session deleted successfully"
250
+ )
251
+ );
252
+ })
253
+ );
254
+ notebooksRouter.get(
255
+ "/v1/sessions/:sessionId/documents",
256
+ generalRateLimiter,
257
+ requireNotebooksPermission,
258
+ requireSessionOwnership(),
259
+ withAuth(async (req, res) => {
260
+ const { sessionId } = req.params;
261
+ const fileType = req.query.fileType;
262
+ const documents = await documentService$1.listDocuments(
263
+ sessionId,
264
+ fileType
265
+ );
266
+ res.json(notebooksTypes.createDocumentListResponse(sessionId, documents));
267
+ })
268
+ );
269
+ notebooksRouter.put(
270
+ "/v1/sessions/:sessionId/documents",
271
+ expensiveRateLimiter,
272
+ requireNotebooksPermission,
273
+ constant.upload.single("file"),
274
+ withAuth(async (req, res, userId) => {
275
+ const { sessionId } = req.params;
276
+ const { fileType, title, newTitle } = req.body;
277
+ if (!title) {
278
+ utils.handleError(logger, res, "title is required");
279
+ return;
280
+ }
281
+ if (!fileType || !documentHelpers.isValidFileType(fileType)) {
282
+ utils.handleError(
283
+ logger,
284
+ res,
285
+ `Unsupported file type: ${fileType}. Supported types: md, txt, pdf, json, yaml, yml, log`
286
+ );
287
+ return;
288
+ }
289
+ const session = await sessionService$1.readSession(sessionId, userId);
290
+ if (!session) {
291
+ utils.handleError(logger, res, "Session not found");
292
+ return;
293
+ }
294
+ const parsedDocument = await documentHelpers.parseFileContent(logger, fileType, req.file);
295
+ const fileId = await documentService$1.uploadFile(
296
+ parsedDocument.content,
297
+ title
298
+ );
299
+ res.status(constant.HTTP_STATUS_ACCEPTED).json({
300
+ status: "processing",
301
+ document_id: newTitle || title,
302
+ session_id: sessionId,
303
+ message: "Document upload started"
304
+ });
305
+ const docName = newTitle || title;
306
+ documentService$1.upsertDocument(sessionId, title, fileType, fileId, newTitle).then(() => logger.info(`Background upload succeeded: ${docName}`)).catch(
307
+ (err) => logger.error(`Background upload failed: ${docName}`, err)
308
+ );
309
+ })
310
+ );
311
+ notebooksRouter.get(
312
+ "/v1/sessions/:sessionId/documents/:documentId/status",
313
+ generalRateLimiter,
314
+ requireNotebooksPermission,
315
+ requireSessionOwnership(),
316
+ withAuth(async (req, res) => {
317
+ const { sessionId, documentId } = req.params;
318
+ const fileStatus = await documentService$1.getFileStatus(
319
+ sessionId,
320
+ documentId
321
+ );
322
+ res.json({
323
+ status: fileStatus.status,
324
+ document_id: documentId,
325
+ session_id: sessionId,
326
+ ...fileStatus.error && { error: fileStatus.error }
327
+ });
328
+ })
329
+ );
330
+ notebooksRouter.delete(
331
+ "/v1/sessions/:sessionId/documents/:documentId",
332
+ generalRateLimiter,
333
+ requireNotebooksPermission,
334
+ requireSessionOwnership(),
335
+ withAuth(async (req, res) => {
336
+ const { sessionId, documentId } = req.params;
337
+ await documentService$1.deleteDocument(sessionId, documentId);
338
+ res.json(
339
+ notebooksTypes.createDocumentResponse(
340
+ documentId,
341
+ sessionId,
342
+ "Document deleted successfully"
343
+ )
344
+ );
345
+ })
346
+ );
347
+ const validateNotebooksQuery = (query) => {
348
+ if (!query) return "query is required";
349
+ if (typeof query === "string" && query.length > constant.MAX_QUERY_LENGTH) {
350
+ return `query exceeds maximum length of ${constant.MAX_QUERY_LENGTH} characters`;
351
+ }
352
+ return null;
353
+ };
354
+ notebooksRouter.post(
355
+ "/v1/sessions/:sessionId/query",
356
+ expensiveRateLimiter,
357
+ requireNotebooksPermission,
358
+ express__default.default.json({ limit: constant.EXPRESS_JSON_BODY_LIMIT }),
359
+ withAuth(async (req, res, userId) => {
360
+ const { sessionId } = req.params;
361
+ const { query } = req.body;
362
+ const queryError = validateNotebooksQuery(query);
363
+ if (queryError) {
364
+ utils.handleError(logger, res, queryError);
365
+ return;
366
+ }
367
+ const session = await sessionService$1.readSession(sessionId, userId);
368
+ let conversationId = session.metadata?.conversation_id;
369
+ const lcsRequest = {
370
+ input: query,
371
+ instructions: systemPrompt,
372
+ tools: [{ type: "file_search", vector_store_ids: [sessionId] }],
373
+ tool_choice: "required",
374
+ model: `${queryProvider}/${queryModel}`,
375
+ stream: true,
376
+ temperature: 0.35,
377
+ shield_ids: [],
378
+ max_tool_calls: 15,
379
+ ...conversationId && { conversation: conversationId }
380
+ };
381
+ for (let retries = 0; retries <= constant.MAX_QUERY_RETRIES; retries++) {
382
+ const abortController = new AbortController();
383
+ const response = await fetch(
384
+ `${lcsBaseUrl}/v1/responses?user_id=${encodeURIComponent(userId)}`,
385
+ {
386
+ method: "POST",
387
+ headers: { "Content-Type": "application/json" },
388
+ body: JSON.stringify(lcsRequest),
389
+ signal: abortController.signal
390
+ }
391
+ );
392
+ if (!response.ok && conversationId && response.status === 404 && retries === 0) {
393
+ logger.warn(`Conversation ${conversationId} not found - retrying`);
394
+ await sessionService$1.updateSession(
395
+ sessionId,
396
+ userId,
397
+ void 0,
398
+ void 0,
399
+ {
400
+ ...session.metadata,
401
+ conversation_id: null
402
+ }
403
+ );
404
+ delete lcsRequest.conversation;
405
+ conversationId = null;
406
+ continue;
407
+ }
408
+ if (!response.ok) {
409
+ const errorBody = await response.json();
410
+ const errorMsg = errorBody?.detail?.[0]?.msg || errorBody?.detail?.cause || "Unknown error";
411
+ const statusCode = response.status >= 400 && response.status < 500 ? response.status : constant.HTTP_STATUS_INTERNAL_ERROR;
412
+ res.status(statusCode).json({
413
+ status: "error",
414
+ error: `Error from Lightspeed Core: ${errorMsg}`
415
+ });
416
+ return;
417
+ }
418
+ if (response.body) {
419
+ const body = stream.Readable.fromWeb(response.body);
420
+ const transformStream = createResponsesApiTransform(
421
+ session,
422
+ sessionId,
423
+ userId
424
+ );
425
+ body.on("error", (error) => {
426
+ logger.error(
427
+ `Upstream stream error while processing notebook query: ${error}`
428
+ );
429
+ if (res.headersSent) {
430
+ res.destroy();
431
+ } else {
432
+ res.status(500).json({ status: "error", error: "Stream error occurred" });
433
+ }
434
+ abortController.abort();
435
+ transformStream.destroy();
436
+ });
437
+ transformStream.on("error", (error) => {
438
+ logger.error(
439
+ `Transform stream error while processing notebook query: ${error}`
440
+ );
441
+ if (res.headersSent) {
442
+ res.destroy();
443
+ } else {
444
+ res.status(500).json({
445
+ status: "error",
446
+ error: "Processing error occurred"
447
+ });
448
+ }
449
+ body.destroy();
450
+ abortController.abort();
451
+ });
452
+ res.on("close", () => {
453
+ if (!res.writableFinished) {
454
+ logger.warn(
455
+ "Client disconnected while processing notebook query"
456
+ );
457
+ abortController.abort();
458
+ body.destroy();
459
+ transformStream.destroy();
460
+ }
461
+ });
462
+ body.pipe(transformStream).pipe(res);
463
+ }
464
+ break;
465
+ }
466
+ })
467
+ );
468
+ notebooksRouter.use(
469
+ (err, req, res, _next) => utils.handleError(logger, res, err, req.path)
470
+ );
471
+ const router = express.Router();
472
+ router.use("/notebooks", notebooksRouter);
473
+ return router;
474
+ }
475
+
476
+ exports.createNotebooksRouter = createNotebooksRouter;
477
+ //# sourceMappingURL=notebooksRouters.cjs.js.map
@@ -0,0 +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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"}