@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend 4.0.0 → 4.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend
2
2
 
3
+ ## 4.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6c8f76d: Removed public `iaSavedPromptsManagePermission` from the common package. Saved-prompts backend routes now require `intelligent-assistant.chat.use`. Operators should drop `intelligent-assistant.saved-prompts.manage` from RBAC CSVs; `chat.use` is enough.
8
+
9
+ ### Patch Changes
10
+
11
+ - c0d97e4: Removed unused `@langchain/core` and `@langchain/openai` dependencies left over after the backend switched from an in-process LangChain client to the Lightspeed Core HTTP proxy.
12
+ - e5deac5: AI Notebooks will use markitdown to clean up data before vectorizing documents
13
+ - Updated dependencies [6c8f76d]
14
+ - @red-hat-developer-hub/backstage-plugin-intelligent-assistant-common@4.1.0
15
+
3
16
  ## 4.0.0
4
17
 
5
18
  ### Major Changes
package/README.md CHANGED
@@ -345,9 +345,6 @@ p, role:default/team_a, intelligent-assistant.notebooks.manage, use, allow
345
345
  p, role:default/team_a, mcp.tools.use, use, allow
346
346
  p, role:default/team_a, mcp.tools.manage, use, allow
347
347
 
348
- # Required for saved prompts
349
- p, role:default/team_a, intelligent-assistant.saved-prompts.manage, update, allow
350
-
351
348
  g, user:default/<your-user-name>, role:default/team_a
352
349
 
353
350
  ```
@@ -5,9 +5,6 @@ var migration = require('./database/migration.cjs.js');
5
5
  var notebooksRouters = require('./service/notebooks/notebooksRouters.cjs.js');
6
6
  require('@backstage/errors');
7
7
  require('./service/constant.cjs.js');
8
- require('js-yaml');
9
- require('pdfjs-dist');
10
- require('stream');
11
8
  var router = require('./service/router.cjs.js');
12
9
 
13
10
  const intelligentAssistantPlugin = backendPluginApi.createBackendPlugin({
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\n\nimport { migrate } from './database/migration';\nimport { createNotebooksRouter } from './service/notebooks';\nimport { createRouter } from './service/router';\n\n/**\n * @public\n * The lightspeed backend plugin.\n */\nexport const intelligentAssistantPlugin = createBackendPlugin({\n pluginId: 'intelligent-assistant',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n http: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n auth: coreServices.auth,\n userInfo: coreServices.userInfo,\n permissions: coreServices.permissions,\n database: coreServices.database,\n },\n async init({\n logger,\n config,\n http,\n httpAuth,\n auth,\n userInfo,\n permissions,\n database,\n }) {\n await migrate(database);\n\n if (config.has('lightspeed')) {\n logger.warn(\n 'DEPRECATED: The \"lightspeed\" configuration key has been renamed to \"intelligent-assistant\". ' +\n 'Please update your app-config.yaml. The old \"lightspeed\" key is no longer read. ' +\n 'Migration guide: https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/README.md#migration-from-lightspeed-to-intelligent-assistant',\n );\n }\n\n const aiNotebooksEnabled =\n config.getOptionalBoolean(\n 'intelligent-assistant.notebooks.enabled',\n ) ?? false;\n\n if (aiNotebooksEnabled) {\n const queryModel = config.getOptionalString(\n 'intelligent-assistant.notebooks.queryDefaults.model',\n );\n const queryProvider = config.getOptionalString(\n 'intelligent-assistant.notebooks.queryDefaults.provider_id',\n );\n\n if (!queryModel || !queryProvider) {\n logger.warn(\n 'AI Notebooks feature is enabled but required configuration is missing. ' +\n 'Please configure intelligent-assistant.notebooks.queryDefaults.model and intelligent-assistant.notebooks.queryDefaults.provider_id. ' +\n 'Notebooks will not be available until these are set.',\n );\n } else {\n http.use(\n await createNotebooksRouter({\n config: config,\n logger: logger,\n httpAuth: httpAuth,\n userInfo: userInfo,\n permissions,\n }),\n );\n logger.info('AI Notebooks enabled');\n\n http.addAuthPolicy({\n path: '/notebooks/health',\n allow: 'unauthenticated',\n });\n }\n }\n\n http.use(\n await createRouter({\n config,\n logger,\n database,\n httpAuth,\n auth,\n userInfo,\n permissions,\n }),\n );\n\n // Configure authentication policies\n http.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","migrate","createNotebooksRouter","createRouter"],"mappings":";;;;;;;;;;;;AA6BO,MAAM,6BAA6BA,oCAAA,CAAoB;AAAA,EAC5D,QAAA,EAAU,uBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,MAAMA,6BAAA,CAAa,UAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,MAAMA,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,aAAaA,6BAAA,CAAa,WAAA;AAAA,QAC1B,UAAUA,6BAAA,CAAa;AAAA,OACzB;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAMC,kBAAQ,QAAQ,CAAA;AAEtB,QAAA,IAAI,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC5B,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WAGF;AAAA,QACF;AAEA,QAAA,MAAM,qBACJ,MAAA,CAAO,kBAAA;AAAA,UACL;AAAA,SACF,IAAK,KAAA;AAEP,QAAA,IAAI,kBAAA,EAAoB;AACtB,UAAA,MAAM,aAAa,MAAA,CAAO,iBAAA;AAAA,YACxB;AAAA,WACF;AACA,UAAA,MAAM,gBAAgB,MAAA,CAAO,iBAAA;AAAA,YAC3B;AAAA,WACF;AAEA,UAAA,IAAI,CAAC,UAAA,IAAc,CAAC,aAAA,EAAe;AACjC,YAAA,MAAA,CAAO,IAAA;AAAA,cACL;AAAA,aAGF;AAAA,UACF,CAAA,MAAO;AACL,YAAA,IAAA,CAAK,GAAA;AAAA,cACH,MAAMC,sCAAA,CAAsB;AAAA,gBAC1B,MAAA;AAAA,gBACA,MAAA;AAAA,gBACA,QAAA;AAAA,gBACA,QAAA;AAAA,gBACA;AAAA,eACD;AAAA,aACH;AACA,YAAA,MAAA,CAAO,KAAK,sBAAsB,CAAA;AAElC,YAAA,IAAA,CAAK,aAAA,CAAc;AAAA,cACjB,IAAA,EAAM,mBAAA;AAAA,cACN,KAAA,EAAO;AAAA,aACR,CAAA;AAAA,UACH;AAAA,QACF;AAEA,QAAA,IAAA,CAAK,GAAA;AAAA,UACH,MAAMC,mBAAA,CAAa;AAAA,YACjB,MAAA;AAAA,YACA,MAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,IAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAGA,QAAA,IAAA,CAAK,aAAA,CAAc;AAAA,UACjB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\n\nimport { migrate } from './database/migration';\nimport { createNotebooksRouter } from './service/notebooks';\nimport { createRouter } from './service/router';\n\n/**\n * @public\n * The lightspeed backend plugin.\n */\nexport const intelligentAssistantPlugin = createBackendPlugin({\n pluginId: 'intelligent-assistant',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n http: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n auth: coreServices.auth,\n userInfo: coreServices.userInfo,\n permissions: coreServices.permissions,\n database: coreServices.database,\n },\n async init({\n logger,\n config,\n http,\n httpAuth,\n auth,\n userInfo,\n permissions,\n database,\n }) {\n await migrate(database);\n\n if (config.has('lightspeed')) {\n logger.warn(\n 'DEPRECATED: The \"lightspeed\" configuration key has been renamed to \"intelligent-assistant\". ' +\n 'Please update your app-config.yaml. The old \"lightspeed\" key is no longer read. ' +\n 'Migration guide: https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/README.md#migration-from-lightspeed-to-intelligent-assistant',\n );\n }\n\n const aiNotebooksEnabled =\n config.getOptionalBoolean(\n 'intelligent-assistant.notebooks.enabled',\n ) ?? false;\n\n if (aiNotebooksEnabled) {\n const queryModel = config.getOptionalString(\n 'intelligent-assistant.notebooks.queryDefaults.model',\n );\n const queryProvider = config.getOptionalString(\n 'intelligent-assistant.notebooks.queryDefaults.provider_id',\n );\n\n if (!queryModel || !queryProvider) {\n logger.warn(\n 'AI Notebooks feature is enabled but required configuration is missing. ' +\n 'Please configure intelligent-assistant.notebooks.queryDefaults.model and intelligent-assistant.notebooks.queryDefaults.provider_id. ' +\n 'Notebooks will not be available until these are set.',\n );\n } else {\n http.use(\n await createNotebooksRouter({\n config: config,\n logger: logger,\n httpAuth: httpAuth,\n userInfo: userInfo,\n permissions,\n }),\n );\n logger.info('AI Notebooks enabled');\n\n http.addAuthPolicy({\n path: '/notebooks/health',\n allow: 'unauthenticated',\n });\n }\n }\n\n http.use(\n await createRouter({\n config,\n logger,\n database,\n httpAuth,\n auth,\n userInfo,\n permissions,\n }),\n );\n\n // Configure authentication policies\n http.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","migrate","createNotebooksRouter","createRouter"],"mappings":";;;;;;;;;AA6BO,MAAM,6BAA6BA,oCAAA,CAAoB;AAAA,EAC5D,QAAA,EAAU,uBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,QAAQC,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,MAAMA,6BAAA,CAAa,UAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,MAAMA,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,aAAaA,6BAAA,CAAa,WAAA;AAAA,QAC1B,UAAUA,6BAAA,CAAa;AAAA,OACzB;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAMC,kBAAQ,QAAQ,CAAA;AAEtB,QAAA,IAAI,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC5B,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WAGF;AAAA,QACF;AAEA,QAAA,MAAM,qBACJ,MAAA,CAAO,kBAAA;AAAA,UACL;AAAA,SACF,IAAK,KAAA;AAEP,QAAA,IAAI,kBAAA,EAAoB;AACtB,UAAA,MAAM,aAAa,MAAA,CAAO,iBAAA;AAAA,YACxB;AAAA,WACF;AACA,UAAA,MAAM,gBAAgB,MAAA,CAAO,iBAAA;AAAA,YAC3B;AAAA,WACF;AAEA,UAAA,IAAI,CAAC,UAAA,IAAc,CAAC,aAAA,EAAe;AACjC,YAAA,MAAA,CAAO,IAAA;AAAA,cACL;AAAA,aAGF;AAAA,UACF,CAAA,MAAO;AACL,YAAA,IAAA,CAAK,GAAA;AAAA,cACH,MAAMC,sCAAA,CAAsB;AAAA,gBAC1B,MAAA;AAAA,gBACA,MAAA;AAAA,gBACA,QAAA;AAAA,gBACA,QAAA;AAAA,gBACA;AAAA,eACD;AAAA,aACH;AACA,YAAA,MAAA,CAAO,KAAK,sBAAsB,CAAA;AAElC,YAAA,IAAA,CAAK,aAAA,CAAc;AAAA,cACjB,IAAA,EAAM,mBAAA;AAAA,cACN,KAAA,EAAO;AAAA,aACR,CAAA;AAAA,UACH;AAAA,QACF;AAEA,QAAA,IAAA,CAAK,GAAA;AAAA,UACH,MAAMC,mBAAA,CAAa;AAAA,YACjB,MAAA;AAAA,YACA,MAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,IAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAGA,QAAA,IAAA,CAAK,aAAA,CAAc;AAAA,UACjB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -1,36 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var errors = require('@backstage/errors');
4
- require('htmlparser2');
3
+ require('@backstage/errors');
5
4
  require('dns/promises');
6
5
  require('net');
7
6
  var constant = require('../../constant.cjs.js');
8
- var fileParser = require('./fileParser.cjs.js');
9
7
 
10
- const isValidFileSize = (fileSize) => {
11
- const maxSize = constant.DEFAULT_MAX_FILE_SIZE_MB;
12
- return fileSize <= maxSize;
13
- };
14
8
  const isValidFileType = (fileType) => {
15
9
  const normalizedType = fileType.toLowerCase().replace(/^\./, "");
16
10
  return Object.values(constant.SupportedFileType).includes(
17
11
  normalizedType
18
12
  );
19
13
  };
20
- const parseFileContent = async (logger, fileType, file) => {
21
- if (!file) {
22
- throw new errors.InputError("No file uploaded");
23
- }
24
- if (!isValidFileSize(file.size)) {
25
- throw new errors.InputError(
26
- `File size exceeds ${constant.DEFAULT_MAX_FILE_SIZE_MB / 1024 / 1024}MB limit`
27
- );
28
- }
29
- logger.info(`Parsing file ${file.originalname} for fileType ${fileType}`);
30
- return await fileParser.parseFile(file.buffer, file.originalname, fileType);
31
- };
32
14
 
33
- exports.isValidFileSize = isValidFileSize;
34
15
  exports.isValidFileType = isValidFileType;
35
- exports.parseFileContent = parseFileContent;
36
16
  //# sourceMappingURL=documentHelpers.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"documentHelpers.cjs.js","sources":["../../../../src/service/notebooks/documents/documentHelpers.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 */\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\n\nimport { Parser } from 'htmlparser2';\n\nimport dns from 'dns/promises';\nimport { isIP } from 'net';\n\nimport {\n DEFAULT_MAX_FILE_SIZE_MB,\n FILTERED_CONTENT_MARKER,\n HTML_BLOCK_TAGS,\n HTML_IGNORED_TAGS,\n MAX_CONSECUTIVE_NEWLINES,\n PROMPT_INJECTION_PATTERNS,\n SSRF_BLOCKED_HOSTNAMES,\n SupportedFileType,\n} from '../../constant';\nimport { parseFile } from './fileParser';\n\n// ==============================================================================\n// URL Support Functions (Reserved for Future URL File Type Support)\n// ==============================================================================\n// The following functions are currently unused but reserved for future\n// implementation of URL file type parsing. They provide:\n// - URL validation and format checking\n// - SSRF (Server-Side Request Forgery) protection\n// - HTML content parsing and sanitization\n// - Prompt injection attack prevention\n//\n// These functions were part of the URL parsing implementation that was removed\n// but are kept here to facilitate re-implementation if needed in the future.\n// ==============================================================================\n\n/**\n * Validate URL format\n * @reserved Reserved for future URL file type support\n */\nexport const isValidURL = (urlString: string): boolean => {\n try {\n const url = new URL(urlString);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Validate file size (max 20MB by default)\n */\nexport const isValidFileSize = (fileSize: number): boolean => {\n const maxSize = DEFAULT_MAX_FILE_SIZE_MB;\n return fileSize <= maxSize;\n};\n\n/**\n * Validate file type\n */\nexport const isValidFileType = (fileType: string): boolean => {\n const normalizedType = fileType.toLowerCase().replace(/^\\./, '');\n return Object.values(SupportedFileType).includes(\n normalizedType as SupportedFileType,\n );\n};\n\n/**\n * Parse file from upload\n * @param logger - Logger service\n * @param fileType - File type\n * @param file - File to parse\n * @returns Parsed file\n */\nexport const parseFileContent = async (\n logger: LoggerService,\n fileType: string,\n file?: Express.Multer.File,\n) => {\n if (!file) {\n throw new InputError('No file uploaded');\n }\n if (!isValidFileSize(file.size)) {\n throw new InputError(\n `File size exceeds ${DEFAULT_MAX_FILE_SIZE_MB / 1024 / 1024}MB limit`,\n );\n }\n logger.info(`Parsing file ${file.originalname} for fileType ${fileType}`);\n return await parseFile(file.buffer, file.originalname, fileType);\n};\n\n/**\n * Check if an IP address is private, internal, or a metadata endpoint\n * Blocks SSRF attacks to internal networks\n * @reserved Reserved for future URL file type support\n */\nconst isPrivateOrInternalIP = (ip: string): boolean => {\n // Check if it's a valid IP\n const ipVersion = isIP(ip);\n if (ipVersion === 0) {\n return false; // Not a valid IP\n }\n\n if (ipVersion === 4) {\n const parts = ip.split('.').map(Number);\n\n switch (parts[0]) {\n case 0: // 0.0.0.0/8 - Current network\n case 10: // 10.0.0.0/8 - Private\n case 127: // 127.0.0.0/8 - Loopback\n return true;\n case 100: // 100.64.0.0/10 - Carrier-grade NAT\n return parts[1] >= 64 && parts[1] <= 127;\n case 169: // 169.254.0.0/16 - Link-local (includes cloud metadata endpoint 169.254.169.254)\n return parts[1] === 254;\n case 172: // 172.16.0.0/12 - Private\n return parts[1] >= 16 && parts[1] <= 31;\n case 192: // 192.168.0.0/16 - Private\n return parts[1] === 168;\n default:\n // 224.0.0.0/4 - Multicast, 240.0.0.0/4 - Reserved\n return parts[0] >= 224;\n }\n } else if (ipVersion === 6) {\n const lower = ip.toLowerCase();\n\n // ::1 - Loopback\n if (lower === '::1' || lower === '0:0:0:0:0:0:0:1') return true;\n\n // fc00::/7 - Unique local address\n if (lower.startsWith('fc') || lower.startsWith('fd')) return true;\n\n // fe80::/10 - Link-local\n if (\n lower.startsWith('fe8') ||\n lower.startsWith('fe9') ||\n lower.startsWith('fea') ||\n lower.startsWith('feb')\n )\n return true;\n\n // ::ffff:0:0/96 - IPv4-mapped IPv6\n if (lower.startsWith('::ffff:')) {\n const ipv4Part = ip.substring(7);\n // Check if it's in dotted-decimal format (e.g., ::ffff:127.0.0.1)\n if (ipv4Part.includes('.')) {\n return isPrivateOrInternalIP(ipv4Part);\n }\n // Handle hex format (e.g., ::ffff:7f00:1 which is 127.0.0.1)\n // Extract the hex parts and check against private ranges\n const parts = ipv4Part.split(':');\n if (parts.length >= 1) {\n const firstHex = parseInt(parts[0], 16);\n // Check for common private ranges in hex:\n // 127.x.x.x -> 0x7F00-0x7FFF (loopback)\n // 10.x.x.x -> 0x0A00-0x0AFF (private)\n // 192.168.x.x -> 0xC0A8 (private)\n // 172.16-31.x.x -> 0xAC10-0xAC1F (private)\n // 169.254.x.x -> 0xA9FE (link-local)\n if (\n (firstHex >= 0x7f00 && firstHex <= 0x7fff) || // 127.x.x.x\n (firstHex >= 0x0a00 && firstHex <= 0x0aff) || // 10.x.x.x\n firstHex === 0xc0a8 || // 192.168.x.x\n (firstHex >= 0xac10 && firstHex <= 0xac1f) || // 172.16-31.x.x\n firstHex === 0xa9fe // 169.254.x.x\n ) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\n/**\n * Validate URL and check for SSRF (Server-Side Request Forgery) vulnerabilities\n *\n * This function protects against SSRF attacks by:\n * 1. Blocking access to private/internal IP ranges (RFC 1918, link-local, loopback)\n * 2. Blocking access to cloud metadata endpoints (AWS, GCP, Azure)\n * 3. Resolving hostnames to IPs to prevent DNS rebinding attacks\n *\n * @param urlString - URL to validate\n * @throws InputError if URL points to blocked hostname or private IP\n * @reserved Reserved for future URL file type support\n */\nexport const validateURLForSSRF = async (urlString: string): Promise<void> => {\n const url = new URL(urlString);\n\n // Strip brackets from IPv6 addresses (e.g., [::1] -> ::1)\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n\n // Check if hostname is already an IP address\n const ipVersion = isIP(hostname);\n if (ipVersion !== 0) {\n if (isPrivateOrInternalIP(hostname)) {\n throw new InputError(\n 'Access to private/internal IP addresses is not allowed',\n );\n }\n return;\n }\n\n // Block localhost and common internal hostnames used in SSRF attacks\n const lowerHostname = hostname.toLowerCase();\n if (SSRF_BLOCKED_HOSTNAMES.includes(lowerHostname as any)) {\n throw new InputError(`Access to ${lowerHostname} is not allowed`);\n }\n\n // Resolve hostname to IP addresses\n try {\n const addresses = await dns.resolve(hostname);\n\n // Check all resolved IPs\n for (const address of addresses) {\n if (isPrivateOrInternalIP(address)) {\n throw new InputError(\n `URL resolves to private/internal IP address (${address}), which is not allowed`,\n );\n }\n }\n } catch (error: any) {\n // Re-throw InputError from isPrivateOrInternalIP check\n if (error instanceof InputError) {\n throw error;\n }\n // DNS resolution failure is a user input issue (invalid hostname)\n throw new InputError(`Failed to resolve hostname: ${error.message}`);\n }\n};\n\n/**\n * Strip HTML tags and extract readable text from HTML content\n * @reserved Reserved for future URL file type support\n */\nexport const stripHtmlTags = (html: string): string => {\n let text = '';\n let ignoring = false;\n\n const parser = new Parser(\n {\n onopentag(name) {\n if (HTML_IGNORED_TAGS.has(name)) ignoring = true;\n },\n onclosetag(name) {\n if (HTML_IGNORED_TAGS.has(name)) ignoring = false;\n if (HTML_BLOCK_TAGS.has(name)) text += '\\n';\n },\n ontext(data) {\n if (!ignoring) text += data;\n },\n },\n { decodeEntities: true },\n );\n\n parser.write(html);\n parser.end();\n\n return text\n .replace(/\\n\\s*\\n/g, '\\n\\n')\n .replace(/[ \\t]+/g, ' ')\n .trim();\n};\n\n/**\n * Sanitize content to prevent prompt injection attacks\n * Detects and filters common prompt injection patterns\n * @param content - Raw content to sanitize\n * @returns Sanitized content with prompt injection patterns removed\n * @reserved Reserved for future URL file type support\n */\nexport const sanitizeContentForRAG = (content: string): string => {\n let sanitized = content;\n\n // Replace prompt injection patterns with filtered marker\n for (const pattern of PROMPT_INJECTION_PATTERNS) {\n sanitized = sanitized.replace(pattern, FILTERED_CONTENT_MARKER);\n }\n\n // Limit excessive consecutive newlines to prevent context stuffing\n const newlinePattern = new RegExp(\n `\\\\n{${MAX_CONSECUTIVE_NEWLINES + 1},}`,\n 'g',\n );\n sanitized = sanitized.replace(\n newlinePattern,\n '\\n'.repeat(MAX_CONSECUTIVE_NEWLINES),\n );\n\n // Normalize excessive whitespace\n sanitized = sanitized.replace(/[ \\t]{10,}/g, ' ');\n\n return sanitized.trim();\n};\n"],"names":["DEFAULT_MAX_FILE_SIZE_MB","SupportedFileType","InputError","parseFile"],"mappings":";;;;;;;;;AAiEO,MAAM,eAAA,GAAkB,CAAC,QAAA,KAA8B;AAC5D,EAAA,MAAM,OAAA,GAAUA,iCAAA;AAChB,EAAA,OAAO,QAAA,IAAY,OAAA;AACrB;AAKO,MAAM,eAAA,GAAkB,CAAC,QAAA,KAA8B;AAC5D,EAAA,MAAM,iBAAiB,QAAA,CAAS,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA,CAAOC,0BAAiB,CAAA,CAAE,QAAA;AAAA,IACtC;AAAA,GACF;AACF;AASO,MAAM,gBAAA,GAAmB,OAC9B,MAAA,EACA,QAAA,EACA,IAAA,KACG;AACH,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,MAAM,IAAIC,kBAAW,kBAAkB,CAAA;AAAA,EACzC;AACA,EAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,CAAA,kBAAA,EAAqBF,iCAAA,GAA2B,IAAA,GAAO,IAAI,CAAA,QAAA;AAAA,KAC7D;AAAA,EACF;AACA,EAAA,MAAA,CAAO,KAAK,CAAA,aAAA,EAAgB,IAAA,CAAK,YAAY,CAAA,cAAA,EAAiB,QAAQ,CAAA,CAAE,CAAA;AACxE,EAAA,OAAO,MAAMG,oBAAA,CAAU,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,cAAc,QAAQ,CAAA;AACjE;;;;;;"}
1
+ {"version":3,"file":"documentHelpers.cjs.js","sources":["../../../../src/service/notebooks/documents/documentHelpers.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 */\nimport { InputError } from '@backstage/errors';\n\nimport dns from 'dns/promises';\nimport { isIP } from 'net';\n\nimport {\n DEFAULT_MAX_FILE_SIZE_MB,\n FILTERED_CONTENT_MARKER,\n MAX_CONSECUTIVE_NEWLINES,\n PROMPT_INJECTION_PATTERNS,\n SSRF_BLOCKED_HOSTNAMES,\n SupportedFileType,\n} from '../../constant';\n\n// ==============================================================================\n// URL Support Functions (Reserved for Future URL File Type Support)\n// ==============================================================================\n// The following functions are currently unused but reserved for future\n// implementation of URL file type parsing. They provide:\n// - URL validation and format checking\n// - SSRF (Server-Side Request Forgery) protection\n// - HTML content parsing and sanitization\n// - Prompt injection attack prevention\n//\n// These functions were part of the URL parsing implementation that was removed\n// but are kept here to facilitate re-implementation if needed in the future.\n// ==============================================================================\n\n/**\n * Validate URL format\n * @reserved Reserved for future URL file type support\n */\nexport const isValidURL = (urlString: string): boolean => {\n try {\n const url = new URL(urlString);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Validate file size (max 20MB by default)\n */\nexport const isValidFileSize = (fileSize: number): boolean => {\n const maxSize = DEFAULT_MAX_FILE_SIZE_MB;\n return fileSize <= maxSize;\n};\n\n/**\n * Validate file type\n */\nexport const isValidFileType = (fileType: string): boolean => {\n const normalizedType = fileType.toLowerCase().replace(/^\\./, '');\n return Object.values(SupportedFileType).includes(\n normalizedType as SupportedFileType,\n );\n};\n\n/**\n * Check if an IP address is private, internal, or a metadata endpoint\n * Blocks SSRF attacks to internal networks\n * @reserved Reserved for future URL file type support\n */\nconst isPrivateOrInternalIP = (ip: string): boolean => {\n // Check if it's a valid IP\n const ipVersion = isIP(ip);\n if (ipVersion === 0) {\n return false; // Not a valid IP\n }\n\n if (ipVersion === 4) {\n const parts = ip.split('.').map(Number);\n\n switch (parts[0]) {\n case 0: // 0.0.0.0/8 - Current network\n case 10: // 10.0.0.0/8 - Private\n case 127: // 127.0.0.0/8 - Loopback\n return true;\n case 100: // 100.64.0.0/10 - Carrier-grade NAT\n return parts[1] >= 64 && parts[1] <= 127;\n case 169: // 169.254.0.0/16 - Link-local (includes cloud metadata endpoint 169.254.169.254)\n return parts[1] === 254;\n case 172: // 172.16.0.0/12 - Private\n return parts[1] >= 16 && parts[1] <= 31;\n case 192: // 192.168.0.0/16 - Private\n return parts[1] === 168;\n default:\n // 224.0.0.0/4 - Multicast, 240.0.0.0/4 - Reserved\n return parts[0] >= 224;\n }\n } else if (ipVersion === 6) {\n const lower = ip.toLowerCase();\n\n // ::1 - Loopback\n if (lower === '::1' || lower === '0:0:0:0:0:0:0:1') return true;\n\n // fc00::/7 - Unique local address\n if (lower.startsWith('fc') || lower.startsWith('fd')) return true;\n\n // fe80::/10 - Link-local\n if (\n lower.startsWith('fe8') ||\n lower.startsWith('fe9') ||\n lower.startsWith('fea') ||\n lower.startsWith('feb')\n )\n return true;\n\n // ::ffff:0:0/96 - IPv4-mapped IPv6\n if (lower.startsWith('::ffff:')) {\n const ipv4Part = ip.substring(7);\n // Check if it's in dotted-decimal format (e.g., ::ffff:127.0.0.1)\n if (ipv4Part.includes('.')) {\n return isPrivateOrInternalIP(ipv4Part);\n }\n // Handle hex format (e.g., ::ffff:7f00:1 which is 127.0.0.1)\n // Extract the hex parts and check against private ranges\n const parts = ipv4Part.split(':');\n if (parts.length >= 1) {\n const firstHex = parseInt(parts[0], 16);\n // Check for common private ranges in hex:\n // 127.x.x.x -> 0x7F00-0x7FFF (loopback)\n // 10.x.x.x -> 0x0A00-0x0AFF (private)\n // 192.168.x.x -> 0xC0A8 (private)\n // 172.16-31.x.x -> 0xAC10-0xAC1F (private)\n // 169.254.x.x -> 0xA9FE (link-local)\n if (\n (firstHex >= 0x7f00 && firstHex <= 0x7fff) || // 127.x.x.x\n (firstHex >= 0x0a00 && firstHex <= 0x0aff) || // 10.x.x.x\n firstHex === 0xc0a8 || // 192.168.x.x\n (firstHex >= 0xac10 && firstHex <= 0xac1f) || // 172.16-31.x.x\n firstHex === 0xa9fe // 169.254.x.x\n ) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\n/**\n * Validate URL and check for SSRF (Server-Side Request Forgery) vulnerabilities\n *\n * This function protects against SSRF attacks by:\n * 1. Blocking access to private/internal IP ranges (RFC 1918, link-local, loopback)\n * 2. Blocking access to cloud metadata endpoints (AWS, GCP, Azure)\n * 3. Resolving hostnames to IPs to prevent DNS rebinding attacks\n *\n * @param urlString - URL to validate\n * @throws InputError if URL points to blocked hostname or private IP\n * @reserved Reserved for future URL file type support\n */\nexport const validateURLForSSRF = async (urlString: string): Promise<void> => {\n const url = new URL(urlString);\n\n // Strip brackets from IPv6 addresses (e.g., [::1] -> ::1)\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n\n // Check if hostname is already an IP address\n const ipVersion = isIP(hostname);\n if (ipVersion !== 0) {\n if (isPrivateOrInternalIP(hostname)) {\n throw new InputError(\n 'Access to private/internal IP addresses is not allowed',\n );\n }\n return;\n }\n\n // Block localhost and common internal hostnames used in SSRF attacks\n const lowerHostname = hostname.toLowerCase();\n if (SSRF_BLOCKED_HOSTNAMES.includes(lowerHostname as any)) {\n throw new InputError(`Access to ${lowerHostname} is not allowed`);\n }\n\n // Resolve hostname to IP addresses\n try {\n const addresses = await dns.resolve(hostname);\n\n // Check all resolved IPs\n for (const address of addresses) {\n if (isPrivateOrInternalIP(address)) {\n throw new InputError(\n `URL resolves to private/internal IP address (${address}), which is not allowed`,\n );\n }\n }\n } catch (error: any) {\n // Re-throw InputError from isPrivateOrInternalIP check\n if (error instanceof InputError) {\n throw error;\n }\n // DNS resolution failure is a user input issue (invalid hostname)\n throw new InputError(`Failed to resolve hostname: ${error.message}`);\n }\n};\n\n/**\n * Sanitize content to prevent prompt injection attacks\n * Detects and filters common prompt injection patterns\n * @param content - Raw content to sanitize\n * @returns Sanitized content with prompt injection patterns removed\n * @reserved Reserved for future URL file type support\n */\nexport const sanitizeContentForRAG = (content: string): string => {\n let sanitized = content;\n\n // Replace prompt injection patterns with filtered marker\n for (const pattern of PROMPT_INJECTION_PATTERNS) {\n sanitized = sanitized.replace(pattern, FILTERED_CONTENT_MARKER);\n }\n\n // Limit excessive consecutive newlines to prevent context stuffing\n const newlinePattern = new RegExp(\n `\\\\n{${MAX_CONSECUTIVE_NEWLINES + 1},}`,\n 'g',\n );\n sanitized = sanitized.replace(\n newlinePattern,\n '\\n'.repeat(MAX_CONSECUTIVE_NEWLINES),\n );\n\n // Normalize excessive whitespace\n sanitized = sanitized.replace(/[ \\t]{10,}/g, ' ');\n\n return sanitized.trim();\n};\n"],"names":["SupportedFileType"],"mappings":";;;;;;;AAmEO,MAAM,eAAA,GAAkB,CAAC,QAAA,KAA8B;AAC5D,EAAA,MAAM,iBAAiB,QAAA,CAAS,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/D,EAAA,OAAO,MAAA,CAAO,MAAA,CAAOA,0BAAiB,CAAA,CAAE,QAAA;AAAA,IACtC;AAAA,GACF;AACF;;;;"}
@@ -2,7 +2,6 @@
2
2
 
3
3
  var errors = require('@backstage/errors');
4
4
  var constant = require('../../constant.cjs.js');
5
- var fileParser = require('./fileParser.cjs.js');
6
5
 
7
6
  class DocumentService {
8
7
  logger;
@@ -43,20 +42,21 @@ class DocumentService {
43
42
  ) || null;
44
43
  }
45
44
  /**
46
- * Upload a file to the Files API
47
- * @param content - File content as string
45
+ * Upload markdown content to the Files API
46
+ * @param content - Markdown content string
48
47
  * @param title - File title/name
49
48
  * @returns File ID from the Files API
50
49
  * @throws Error if upload fails
51
50
  */
52
51
  async uploadFile(content, title) {
53
52
  try {
54
- const mimeType = "text/plain";
55
- const txtFilename = `${title.replace(/\.[^.]+$/, "")}.txt`;
53
+ const mdFilename = `${title.replace(/\.[^.]+$/, "")}.md`;
56
54
  const file = await this.client.files.create({
57
- file: await fileParser.toFile(Buffer.from(content, "utf-8"), txtFilename, {
58
- type: mimeType
59
- }),
55
+ file: {
56
+ name: mdFilename,
57
+ buffer: Buffer.from(content, "utf-8"),
58
+ type: "text/markdown"
59
+ },
60
60
  purpose: "assistants"
61
61
  });
62
62
  this.logger.info(
@@ -1 +1 @@
1
- {"version":3,"file":"documentService.cjs.js","sources":["../../../../src/service/notebooks/documents/documentService.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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\nimport {\n DEFAULT_CHUNK_OVERLAP_TOKENS,\n DEFAULT_CHUNKING_STRATEGY_TYPE,\n DEFAULT_MAX_CHUNK_SIZE_TOKENS,\n} from '../../constant';\nimport { SessionDocument, UpsertResult } from '../types/notebooksTypes';\nimport { VectorStoresOperator } from '../VectorStoresOperator';\nimport { toFile } from './fileParser';\n\n/**\n * Service for managing documents within notebook sessions using File-Based API\n * Each session has its own dedicated vector store\n * Uses VectorStoresOperator to proxy through lightspeed-core\n */\nexport class DocumentService {\n private logger: LoggerService;\n private client: VectorStoresOperator;\n private chunkingStrategy: {\n type: string;\n static?: { max_chunk_size_tokens: number; chunk_overlap_tokens: number };\n };\n\n constructor(\n client: VectorStoresOperator,\n logger: LoggerService,\n config?: Config,\n ) {\n this.client = client;\n this.logger = logger;\n\n // Chunking strategy configuration\n const chunkingType =\n config?.getOptionalString(\n 'intelligent-assistant.notebooks.chunkingStrategy.type',\n ) || DEFAULT_CHUNKING_STRATEGY_TYPE;\n\n if (chunkingType === 'static') {\n this.chunkingStrategy = {\n type: 'static',\n static: {\n max_chunk_size_tokens:\n config?.getOptionalNumber(\n 'intelligent-assistant.notebooks.chunkingStrategy.maxChunkSizeTokens',\n ) || DEFAULT_MAX_CHUNK_SIZE_TOKENS,\n chunk_overlap_tokens:\n config?.getOptionalNumber(\n 'intelligent-assistant.notebooks.chunkingStrategy.chunkOverlapTokens',\n ) || DEFAULT_CHUNK_OVERLAP_TOKENS,\n },\n };\n } else {\n this.chunkingStrategy = { type: 'auto' };\n }\n }\n\n /**\n * Find a file by title in vector store\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title to search for\n * @returns File object if found, null otherwise\n */\n async findFileByTitle(\n sessionId: string,\n documentTitle: string,\n ): Promise<any | null> {\n const filesResponse = await this.client.vectorStores.files.list(sessionId);\n return (\n filesResponse.data.find(\n (f: any) => f.attributes?.title === documentTitle,\n ) || null\n );\n }\n\n /**\n * Upload a file to the Files API\n * @param content - File content as string\n * @param title - File title/name\n * @returns File ID from the Files API\n * @throws Error if upload fails\n */\n async uploadFile(content: string, title: string): Promise<string> {\n try {\n // Determine MIME type from file type or default to text/plain\n const mimeType = 'text/plain';\n const txtFilename = `${title.replace(/\\.[^.]+$/, '')}.txt`;\n const file = await this.client.files.create({\n file: await toFile(Buffer.from(content, 'utf-8'), txtFilename, {\n type: mimeType,\n }),\n purpose: 'assistants',\n });\n\n this.logger.info(\n `File created - id: ${file.id}, filename: ${file.filename}`,\n );\n return file.id;\n } catch (error) {\n // Preserve the original error type and message\n if (error instanceof Error) {\n throw error;\n }\n // For non-Error objects, wrap with context\n throw new Error(`Failed to upload file: ${String(error)}`);\n }\n }\n\n /**\n * Upsert a document - create if it doesn't exist, update if it does.\n * Also used for rename-only operations when fileType/fileId are omitted.\n * @param sessionId - Vector store ID\n * @param title - Original document title\n * @param opts - Optional parameters for upload or rename\n * @param opts.fileType - Document source type (derived from existing file when omitted)\n * @param opts.fileId - File ID from Files API (derived from existing file when omitted)\n * @param opts.newTitle - New title for rename operation\n * @returns Upsert result with document ID and status\n * @throws NotFoundError if fileId is omitted and document does not exist\n * @throws ConflictError if newTitle conflicts with existing document\n */\n async upsertDocument(\n sessionId: string,\n title: string,\n opts?: {\n fileType?: string;\n fileId?: string;\n newTitle?: string;\n },\n ): Promise<UpsertResult> {\n const { fileType, fileId, newTitle } = opts || {};\n const existingFile = await this.findFileByTitle(sessionId, title);\n\n // Rename-only mode: no fileId means we must have an existing file\n let resolvedFileId: string;\n let resolvedFileType: string;\n if (fileId) {\n resolvedFileId = fileId;\n resolvedFileType = fileType || 'unknown';\n } else {\n if (!existingFile) {\n throw new NotFoundError(`Document not found: ${title}`);\n }\n resolvedFileId = existingFile.id;\n resolvedFileType =\n fileType ||\n (existingFile.attributes?.source_type as string) ||\n 'unknown';\n }\n\n const createdAt =\n (existingFile?.attributes?.created_at as string) ||\n new Date().toISOString();\n\n if (newTitle && title !== newTitle) {\n const conflictingFile = await this.findFileByTitle(sessionId, newTitle);\n if (conflictingFile) {\n throw new ConflictError(\n `A resource with the title \"${newTitle || title}\" already exists in this session`,\n );\n }\n }\n\n if (existingFile) {\n await this.deleteDocument(sessionId, title);\n }\n\n // Preserve existing attributes, override with known fields\n const baseAttrs = existingFile?.attributes || {};\n const attributes = {\n ...baseAttrs,\n title: newTitle || title,\n source_type: resolvedFileType,\n created_at: createdAt,\n updated_at: new Date().toISOString(),\n };\n\n try {\n const vectorStoreFile = await this.client.vectorStores.files.create(\n sessionId,\n {\n file_id: resolvedFileId,\n chunking_strategy: this.chunkingStrategy,\n attributes,\n },\n );\n\n this.logger.info(\n `Document \"${newTitle || title}\" (ID: ${title}) upsert started with file ${resolvedFileId}`,\n );\n\n return {\n document_id: newTitle || title,\n file_id: resolvedFileId,\n replaced: !!existingFile,\n status: vectorStoreFile.status,\n };\n } catch (error) {\n // Rollback: restore original entry if delete succeeded but create failed\n if (existingFile) {\n this.logger.error(\n `Failed to re-create vector store entry after delete for \"${title}\". Attempting rollback.`,\n );\n try {\n await this.client.vectorStores.files.create(sessionId, {\n file_id: existingFile.id,\n chunking_strategy: this.chunkingStrategy,\n attributes: existingFile.attributes || {},\n });\n this.logger.info(\n `Rollback succeeded: restored \"${title}\" in session ${sessionId}`,\n );\n } catch (rollbackError) {\n this.logger.error(\n `Rollback failed: document \"${title}\" (file ${existingFile.id}) is orphaned in session ${sessionId}`,\n );\n }\n }\n throw error;\n }\n }\n\n /**\n * Get file processing status\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title\n * @returns File status including processing state, chunk count, and error if any\n * @throws NotFoundError if document not found\n */\n async getFileStatus(\n sessionId: string,\n documentTitle: string,\n ): Promise<{\n status: 'in_progress' | 'completed' | 'failed' | 'cancelled';\n chunks_count: number;\n error?: string;\n }> {\n const file = await this.findFileByTitle(sessionId, documentTitle);\n\n if (!file) {\n throw new NotFoundError(`Resource not found: ${documentTitle}`);\n }\n return {\n status: file.status,\n chunks_count: file.chunks_count,\n error: file.last_error?.message,\n };\n }\n\n /**\n * List all documents in a session\n * @param sessionId - Vector store ID\n * @param fileTypeFilter - Optional filter by source type (text, pdf, url, etc.)\n * @returns Array of session documents\n */\n async listDocuments(\n sessionId: string,\n fileTypeFilter?: string,\n ): Promise<SessionDocument[]> {\n this.logger.info(`Listing documents for session ${sessionId}`);\n\n // List all files in vector store\n const filesResponse = await this.client.vectorStores.files.list(sessionId);\n\n if (!filesResponse.data || filesResponse.data.length === 0) {\n return [];\n }\n\n // Map files to SessionDocument format, sorted by original upload time\n const documents = filesResponse.data\n .filter((file: any) => {\n if (fileTypeFilter && file.attributes?.source_type !== fileTypeFilter) {\n return false;\n }\n return true;\n })\n .map((file: any) => {\n const attrs = file.attributes || {};\n return {\n document_id: attrs.title,\n source_type:\n (attrs.source_type as SessionDocument['source_type']) || 'text',\n created_at: attrs.created_at,\n updated_at: attrs.updated_at,\n };\n })\n .sort((a: SessionDocument, b: SessionDocument) => {\n const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;\n const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;\n return timeA - timeB;\n });\n\n this.logger.info(\n `Found ${documents.length} documents in session ${sessionId}`,\n );\n return documents;\n }\n\n /**\n * Remove a document's vector store entry.\n * Note: the underlying file in the Files API is intentionally preserved\n * so it can be re-associated (e.g., during rename/upsert).\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title to delete\n * @throws NotFoundError if document not found\n */\n async deleteDocument(\n sessionId: string,\n documentTitle: string,\n ): Promise<void> {\n this.logger.info(`Deleting document ${documentTitle} from ${sessionId}`);\n\n const file = await this.findFileByTitle(sessionId, documentTitle);\n\n if (!file) {\n throw new NotFoundError(`Resource not found: ${documentTitle}`);\n }\n\n // Delete from vector store first\n await this.client.vectorStores.files.delete(sessionId, file.id);\n\n this.logger.info(\n `Deleted document ${documentTitle} (file ${file.id}) from session ${sessionId}`,\n );\n }\n}\n"],"names":["DEFAULT_CHUNKING_STRATEGY_TYPE","DEFAULT_MAX_CHUNK_SIZE_TOKENS","DEFAULT_CHUNK_OVERLAP_TOKENS","toFile","NotFoundError","ConflictError"],"mappings":"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"}
1
+ {"version":3,"file":"documentService.cjs.js","sources":["../../../../src/service/notebooks/documents/documentService.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 { LoggerService } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\nimport {\n DEFAULT_CHUNK_OVERLAP_TOKENS,\n DEFAULT_CHUNKING_STRATEGY_TYPE,\n DEFAULT_MAX_CHUNK_SIZE_TOKENS,\n} from '../../constant';\nimport { SessionDocument, UpsertResult } from '../types/notebooksTypes';\nimport { VectorStoresOperator } from '../VectorStoresOperator';\n\n/**\n * Service for managing documents within notebook sessions using File-Based API\n * Each session has its own dedicated vector store\n * Uses VectorStoresOperator to proxy through lightspeed-core\n */\nexport class DocumentService {\n private logger: LoggerService;\n private client: VectorStoresOperator;\n private chunkingStrategy: {\n type: string;\n static?: { max_chunk_size_tokens: number; chunk_overlap_tokens: number };\n };\n\n constructor(\n client: VectorStoresOperator,\n logger: LoggerService,\n config?: Config,\n ) {\n this.client = client;\n this.logger = logger;\n\n // Chunking strategy configuration\n const chunkingType =\n config?.getOptionalString(\n 'intelligent-assistant.notebooks.chunkingStrategy.type',\n ) || DEFAULT_CHUNKING_STRATEGY_TYPE;\n\n if (chunkingType === 'static') {\n this.chunkingStrategy = {\n type: 'static',\n static: {\n max_chunk_size_tokens:\n config?.getOptionalNumber(\n 'intelligent-assistant.notebooks.chunkingStrategy.maxChunkSizeTokens',\n ) || DEFAULT_MAX_CHUNK_SIZE_TOKENS,\n chunk_overlap_tokens:\n config?.getOptionalNumber(\n 'intelligent-assistant.notebooks.chunkingStrategy.chunkOverlapTokens',\n ) || DEFAULT_CHUNK_OVERLAP_TOKENS,\n },\n };\n } else {\n this.chunkingStrategy = { type: 'auto' };\n }\n }\n\n /**\n * Find a file by title in vector store\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title to search for\n * @returns File object if found, null otherwise\n */\n async findFileByTitle(\n sessionId: string,\n documentTitle: string,\n ): Promise<any | null> {\n const filesResponse = await this.client.vectorStores.files.list(sessionId);\n return (\n filesResponse.data.find(\n (f: any) => f.attributes?.title === documentTitle,\n ) || null\n );\n }\n\n /**\n * Upload markdown content to the Files API\n * @param content - Markdown content string\n * @param title - File title/name\n * @returns File ID from the Files API\n * @throws Error if upload fails\n */\n async uploadFile(content: string, title: string): Promise<string> {\n try {\n const mdFilename = `${title.replace(/\\.[^.]+$/, '')}.md`;\n\n const file = await this.client.files.create({\n file: {\n name: mdFilename,\n buffer: Buffer.from(content, 'utf-8'),\n type: 'text/markdown',\n },\n purpose: 'assistants',\n });\n\n this.logger.info(\n `File created - id: ${file.id}, filename: ${file.filename}`,\n );\n return file.id;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Failed to upload file: ${String(error)}`);\n }\n }\n\n /**\n * Upsert a document - create if it doesn't exist, update if it does.\n * Also used for rename-only operations when fileType/fileId are omitted.\n * @param sessionId - Vector store ID\n * @param title - Original document title\n * @param opts - Optional parameters for upload or rename\n * @param opts.fileType - Document source type (derived from existing file when omitted)\n * @param opts.fileId - File ID from Files API (derived from existing file when omitted)\n * @param opts.newTitle - New title for rename operation\n * @returns Upsert result with document ID and status\n * @throws NotFoundError if fileId is omitted and document does not exist\n * @throws ConflictError if newTitle conflicts with existing document\n */\n async upsertDocument(\n sessionId: string,\n title: string,\n opts?: {\n fileType?: string;\n fileId?: string;\n newTitle?: string;\n },\n ): Promise<UpsertResult> {\n const { fileType, fileId, newTitle } = opts || {};\n const existingFile = await this.findFileByTitle(sessionId, title);\n\n // Rename-only mode: no fileId means we must have an existing file\n let resolvedFileId: string;\n let resolvedFileType: string;\n if (fileId) {\n resolvedFileId = fileId;\n resolvedFileType = fileType || 'unknown';\n } else {\n if (!existingFile) {\n throw new NotFoundError(`Document not found: ${title}`);\n }\n resolvedFileId = existingFile.id;\n resolvedFileType =\n fileType ||\n (existingFile.attributes?.source_type as string) ||\n 'unknown';\n }\n\n const createdAt =\n (existingFile?.attributes?.created_at as string) ||\n new Date().toISOString();\n\n if (newTitle && title !== newTitle) {\n const conflictingFile = await this.findFileByTitle(sessionId, newTitle);\n if (conflictingFile) {\n throw new ConflictError(\n `A resource with the title \"${newTitle || title}\" already exists in this session`,\n );\n }\n }\n\n if (existingFile) {\n await this.deleteDocument(sessionId, title);\n }\n\n // Preserve existing attributes, override with known fields\n const baseAttrs = existingFile?.attributes || {};\n const attributes = {\n ...baseAttrs,\n title: newTitle || title,\n source_type: resolvedFileType,\n created_at: createdAt,\n updated_at: new Date().toISOString(),\n };\n\n try {\n const vectorStoreFile = await this.client.vectorStores.files.create(\n sessionId,\n {\n file_id: resolvedFileId,\n chunking_strategy: this.chunkingStrategy,\n attributes,\n },\n );\n\n this.logger.info(\n `Document \"${newTitle || title}\" (ID: ${title}) upsert started with file ${resolvedFileId}`,\n );\n\n return {\n document_id: newTitle || title,\n file_id: resolvedFileId,\n replaced: !!existingFile,\n status: vectorStoreFile.status,\n };\n } catch (error) {\n // Rollback: restore original entry if delete succeeded but create failed\n if (existingFile) {\n this.logger.error(\n `Failed to re-create vector store entry after delete for \"${title}\". Attempting rollback.`,\n );\n try {\n await this.client.vectorStores.files.create(sessionId, {\n file_id: existingFile.id,\n chunking_strategy: this.chunkingStrategy,\n attributes: existingFile.attributes || {},\n });\n this.logger.info(\n `Rollback succeeded: restored \"${title}\" in session ${sessionId}`,\n );\n } catch (rollbackError) {\n this.logger.error(\n `Rollback failed: document \"${title}\" (file ${existingFile.id}) is orphaned in session ${sessionId}`,\n );\n }\n }\n throw error;\n }\n }\n\n /**\n * Get file processing status\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title\n * @returns File status including processing state, chunk count, and error if any\n * @throws NotFoundError if document not found\n */\n async getFileStatus(\n sessionId: string,\n documentTitle: string,\n ): Promise<{\n status: 'in_progress' | 'completed' | 'failed' | 'cancelled';\n chunks_count: number;\n error?: string;\n }> {\n const file = await this.findFileByTitle(sessionId, documentTitle);\n\n if (!file) {\n throw new NotFoundError(`Resource not found: ${documentTitle}`);\n }\n return {\n status: file.status,\n chunks_count: file.chunks_count,\n error: file.last_error?.message,\n };\n }\n\n /**\n * List all documents in a session\n * @param sessionId - Vector store ID\n * @param fileTypeFilter - Optional filter by source type (text, pdf, url, etc.)\n * @returns Array of session documents\n */\n async listDocuments(\n sessionId: string,\n fileTypeFilter?: string,\n ): Promise<SessionDocument[]> {\n this.logger.info(`Listing documents for session ${sessionId}`);\n\n // List all files in vector store\n const filesResponse = await this.client.vectorStores.files.list(sessionId);\n\n if (!filesResponse.data || filesResponse.data.length === 0) {\n return [];\n }\n\n // Map files to SessionDocument format, sorted by original upload time\n const documents = filesResponse.data\n .filter((file: any) => {\n if (fileTypeFilter && file.attributes?.source_type !== fileTypeFilter) {\n return false;\n }\n return true;\n })\n .map((file: any) => {\n const attrs = file.attributes || {};\n return {\n document_id: attrs.title,\n source_type:\n (attrs.source_type as SessionDocument['source_type']) || 'text',\n created_at: attrs.created_at,\n updated_at: attrs.updated_at,\n };\n })\n .sort((a: SessionDocument, b: SessionDocument) => {\n const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;\n const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;\n return timeA - timeB;\n });\n\n this.logger.info(\n `Found ${documents.length} documents in session ${sessionId}`,\n );\n return documents;\n }\n\n /**\n * Remove a document's vector store entry.\n * Note: the underlying file in the Files API is intentionally preserved\n * so it can be re-associated (e.g., during rename/upsert).\n * @param sessionId - Vector store ID\n * @param documentTitle - Document title to delete\n * @throws NotFoundError if document not found\n */\n async deleteDocument(\n sessionId: string,\n documentTitle: string,\n ): Promise<void> {\n this.logger.info(`Deleting document ${documentTitle} from ${sessionId}`);\n\n const file = await this.findFileByTitle(sessionId, documentTitle);\n\n if (!file) {\n throw new NotFoundError(`Resource not found: ${documentTitle}`);\n }\n\n // Delete from vector store first\n await this.client.vectorStores.files.delete(sessionId, file.id);\n\n this.logger.info(\n `Deleted document ${documentTitle} (file ${file.id}) from session ${sessionId}`,\n );\n }\n}\n"],"names":["DEFAULT_CHUNKING_STRATEGY_TYPE","DEFAULT_MAX_CHUNK_SIZE_TOKENS","DEFAULT_CHUNK_OVERLAP_TOKENS","NotFoundError","ConflictError"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+ var markitdownTs = require('markitdown-ts');
5
+
6
+ const markitdown = new markitdownTs.MarkItDown();
7
+ const PLAINTEXT_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".yaml", ".yml", ".log"]);
8
+ async function convertToMarkdown(buffer, originalName, fileType) {
9
+ const normalizedFileType = fileType.toLowerCase();
10
+ const ext = `.${normalizedFileType}`;
11
+ const nameExt = originalName.includes(".") ? `.${originalName.split(".").pop().toLowerCase()}` : "";
12
+ const normalizeExt = (e) => e === ".yml" ? ".yaml" : e;
13
+ if (nameExt && normalizeExt(nameExt) !== normalizeExt(ext)) {
14
+ throw new errors.InputError(
15
+ `File extension "${nameExt}" does not match declared file type "${fileType}"`
16
+ );
17
+ }
18
+ if (PLAINTEXT_EXTENSIONS.has(ext)) {
19
+ return buffer.toString("utf-8");
20
+ }
21
+ const result = await markitdown.convertBuffer(buffer, {
22
+ file_extension: ext
23
+ });
24
+ if (!result?.markdown) {
25
+ throw new errors.InputError(
26
+ `Markdown conversion produced no output for ${originalName}`
27
+ );
28
+ }
29
+ return result.markdown;
30
+ }
31
+
32
+ exports.convertToMarkdown = convertToMarkdown;
33
+ //# sourceMappingURL=markitdownClient.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markitdownClient.cjs.js","sources":["../../../../src/service/notebooks/documents/markitdownClient.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 { InputError } from '@backstage/errors';\n\nimport { MarkItDown } from 'markitdown-ts';\n\nconst markitdown = new MarkItDown();\n\nconst PLAINTEXT_EXTENSIONS = new Set(['.json', '.yaml', '.yml', '.log']);\n\n/**\n * Convert a document buffer to markdown using markitdown-ts.\n * Plain-text formats (json, yaml, log) are passed through as-is.\n */\nexport async function convertToMarkdown(\n buffer: Buffer,\n originalName: string,\n fileType: string,\n): Promise<string> {\n const normalizedFileType = fileType.toLowerCase();\n const ext = `.${normalizedFileType}`;\n\n const nameExt = originalName.includes('.')\n ? `.${originalName.split('.').pop()!.toLowerCase()}`\n : '';\n // Treat .yml and .yaml as equivalent\n const normalizeExt = (e: string) => (e === '.yml' ? '.yaml' : e);\n if (nameExt && normalizeExt(nameExt) !== normalizeExt(ext)) {\n throw new InputError(\n `File extension \"${nameExt}\" does not match declared file type \"${fileType}\"`,\n );\n }\n\n if (PLAINTEXT_EXTENSIONS.has(ext)) {\n return buffer.toString('utf-8');\n }\n\n const result = await markitdown.convertBuffer(buffer, {\n file_extension: ext,\n });\n\n if (!result?.markdown) {\n throw new InputError(\n `Markdown conversion produced no output for ${originalName}`,\n );\n }\n\n return result.markdown;\n}\n"],"names":["MarkItDown","InputError"],"mappings":";;;;;AAoBA,MAAM,UAAA,GAAa,IAAIA,uBAAA,EAAW;AAElC,MAAM,oBAAA,uBAA2B,GAAA,CAAI,CAAC,SAAS,OAAA,EAAS,MAAA,EAAQ,MAAM,CAAC,CAAA;AAMvE,eAAsB,iBAAA,CACpB,MAAA,EACA,YAAA,EACA,QAAA,EACiB;AACjB,EAAA,MAAM,kBAAA,GAAqB,SAAS,WAAA,EAAY;AAChD,EAAA,MAAM,GAAA,GAAM,IAAI,kBAAkB,CAAA,CAAA;AAElC,EAAA,MAAM,OAAA,GAAU,YAAA,CAAa,QAAA,CAAS,GAAG,IACrC,CAAA,CAAA,EAAI,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,EAAI,CAAG,WAAA,EAAa,CAAA,CAAA,GAChD,EAAA;AAEJ,EAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAe,CAAA,KAAM,SAAS,OAAA,GAAU,CAAA;AAC9D,EAAA,IAAI,WAAW,YAAA,CAAa,OAAO,CAAA,KAAM,YAAA,CAAa,GAAG,CAAA,EAAG;AAC1D,IAAA,MAAM,IAAIC,iBAAA;AAAA,MACR,CAAA,gBAAA,EAAmB,OAAO,CAAA,qCAAA,EAAwC,QAAQ,CAAA,CAAA;AAAA,KAC5E;AAAA,EACF;AAEA,EAAA,IAAI,oBAAA,CAAqB,GAAA,CAAI,GAAG,CAAA,EAAG;AACjC,IAAA,OAAO,MAAA,CAAO,SAAS,OAAO,CAAA;AAAA,EAChC;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,UAAA,CAAW,aAAA,CAAc,MAAA,EAAQ;AAAA,IACpD,cAAA,EAAgB;AAAA,GACjB,CAAA;AAED,EAAA,IAAI,CAAC,QAAQ,QAAA,EAAU;AACrB,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,8CAA8C,YAAY,CAAA;AAAA,KAC5D;AAAA,EACF;AAEA,EAAA,OAAO,MAAA,CAAO,QAAA;AAChB;;;;"}
@@ -9,6 +9,7 @@ var getIdentity = require('../middleware/getIdentity.cjs.js');
9
9
  var permission = require('../permission.cjs.js');
10
10
  var documentHelpers = require('./documents/documentHelpers.cjs.js');
11
11
  var documentService = require('./documents/documentService.cjs.js');
12
+ var markitdownClient = require('./documents/markitdownClient.cjs.js');
12
13
  var sessionService = require('./sessions/sessionService.cjs.js');
13
14
  var notebooksTypes = require('./types/notebooksTypes.cjs.js');
14
15
  var utils = require('./utils.cjs.js');
@@ -288,11 +289,16 @@ async function createNotebooksRouter(options) {
288
289
  utils.handleError(logger, res, "Session not found");
289
290
  return;
290
291
  }
291
- const parsedDocument = await documentHelpers.parseFileContent(logger, fileType, req.file);
292
- const fileId = await documentService$1.uploadFile(
293
- parsedDocument.content,
294
- title
292
+ if (!req.file) {
293
+ utils.handleError(logger, res, "No file uploaded");
294
+ return;
295
+ }
296
+ const markdown = await markitdownClient.convertToMarkdown(
297
+ req.file.buffer,
298
+ req.file.originalname,
299
+ fileType
295
300
  );
301
+ const fileId = await documentService$1.uploadFile(markdown, title);
296
302
  res.status(constant.HTTP_STATUS_ACCEPTED).json({
297
303
  status: "processing",
298
304
  document_id: newTitle || title,
@@ -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';\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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"}
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 } from './documents/documentHelpers';\nimport { DocumentService } from './documents/documentService';\nimport { convertToMarkdown } from './documents/markitdownClient';\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\n if (!req.file) {\n handleError(logger, res, 'No file uploaded');\n return;\n }\n\n const markdown = await convertToMarkdown(\n req.file.buffer,\n req.file.originalname,\n fileType,\n );\n const fileId = await documentService.uploadFile(markdown, title);\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 // Attach file 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","convertToMarkdown","HTTP_STATUS_ACCEPTED","createDocumentResponse","MAX_QUERY_LENGTH","EXPRESS_JSON_BODY_LIMIT","MAX_QUERY_RETRIES","HTTP_STATUS_INTERNAL_ERROR","Readable"],"mappings":"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"}
@@ -447,20 +447,20 @@ async function createRouter(options) {
447
447
  router.get(
448
448
  "/v1/saved-prompts/config",
449
449
  generalRateLimiter,
450
- requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
450
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
451
451
  apiProxy
452
452
  // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint
453
453
  );
454
454
  router.get(
455
455
  "/v1/saved-prompts",
456
456
  generalRateLimiter,
457
- requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
457
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
458
458
  apiProxy
459
459
  );
460
460
  router.delete(
461
461
  "/v1/saved-prompts/:prompt_id",
462
462
  generalRateLimiter,
463
- requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
463
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
464
464
  apiProxy
465
465
  );
466
466
  router.get(
@@ -510,7 +510,7 @@ async function createRouter(options) {
510
510
  router.post(
511
511
  "/v1/saved-prompts",
512
512
  generalRateLimiter,
513
- requirePermission(backstagePluginIntelligentAssistantCommon.iaSavedPromptsManagePermission),
513
+ requirePermission(backstagePluginIntelligentAssistantCommon.iaChatUsePermission),
514
514
  async (request, response) => {
515
515
  try {
516
516
  const { userEntityRef } = getIdentity.getIdentity(request);
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.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 { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport type {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport type { BasicPermission } from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\n\nimport express, { Router } from 'express';\nimport { createProxyMiddleware } from 'http-proxy-middleware';\n\nimport {\n iaChatAccessPermission,\n iaChatManagePermission,\n iaChatUsePermission,\n iaMcpManagePermission,\n iaMcpUsePermission,\n iaNotebooksUsePermission,\n iaPermissions,\n iaSavedPromptsManagePermission,\n iaSkillsAccessPermission,\n} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { Readable } from 'node:stream';\n\nimport { ModelCapabilitiesCache } from './attachment-validation';\nimport {\n DEFAULT_LIGHTSPEED_SERVICE_HOST,\n DEFAULT_LIGHTSPEED_SERVICE_PORT,\n EXPRESS_JSON_BODY_LIMIT,\n TEST_VISION_JPEG,\n} from './constant';\nimport { McpUserSettingsStore } from './mcp-server-store';\nimport {\n McpServerAuth,\n McpServerResponse,\n McpServerStatus,\n McpValidationResult,\n} from './mcp-server-types';\nimport { McpServerValidator } from './mcp-server-validator';\nimport { createPermissionMiddleware } from './middleware/createPermissionMiddleware';\nimport { createRateLimitMiddleware } from './middleware/createRateLimitMiddleware';\nimport {\n createIdentityMiddleware,\n getIdentity,\n} from './middleware/getIdentity';\nimport { VectorStoresOperator } from './notebooks/VectorStoresOperator';\nimport { userPermissionAuthorization } from './permission';\nimport { createTokenEncryptor } from './token-encryption';\nimport { QueryRequestBody, RouterOptions } from './types';\nimport { handleLCSFetchError, rewriteProxyPath } from './utils';\nimport {\n validateAttachmentsForModel,\n validateCompletionsRequest,\n} from './validation';\n\n/**\n * MCP Server authentication modes for lightspeed.mcpServers entries.\n *\n * - `'dcr'` — Dynamic Client Registration. The Lightspeed backend mints a\n * short-lived Backstage plugin-request token carrying the current user's\n * identity. This token is forwarded to the MCP server via LCORE's\n * MCP-HEADERS, and the MCP Actions backend validates it against the\n * Backstage Permission Framework (RBAC). Use this for Backstage-internal\n * MCP servers (e.g. `@backstage/plugin-mcp-actions-backend`).\n * A static `token` field is **ignored** when `auth: dcr` is set.\n *\n * - (absent / undefined) — Legacy static-token mode. The token is resolved\n * from the user's DB override first, then from the `token` field in\n * app-config. If neither exists, the server is omitted from MCP-HEADERS.\n * Users can also set their own tokens via the Lightspeed UI.\n */\ninterface StaticMcpServer {\n name: string;\n token?: string;\n auth?: McpServerAuth;\n}\n\n/**\n * Build MCP-HEADERS for LCS. Format matches the LCS \"client\"/\"oauth\" auth\n * model: `{ \"server-name\": { \"Authorization\": \"<token>\" } }`\n *\n * For each admin-configured server:\n *\n * 1. If `auth: 'dcr'`, a Backstage plugin-request token is minted on behalf\n * of the authenticated user. Any static token is ignored.\n *\n * 2. Otherwise, the user's personal token (DB) takes precedence over the\n * admin default from app-config. If neither exists, the server is\n * excluded from MCP-HEADERS (LCORE will skip it or return 401).\n *\n * Servers the user has disabled are always excluded.\n */\nasync function buildMcpHeaders(\n servers: StaticMcpServer[],\n store: McpUserSettingsStore,\n userEntityRef: string,\n options?: {\n dcrAuth?: { authService: AuthService; credentials: BackstageCredentials };\n logger?: LoggerService;\n },\n): Promise<string> {\n const headers: Record<string, { Authorization: string }> = {};\n const userSettings = await store.listByUser(userEntityRef);\n const settingsMap = new Map(userSettings.map(s => [s.server_name, s]));\n\n for (const server of servers) {\n const setting = settingsMap.get(server.name);\n const enabled = setting ? Boolean(setting.enabled) : true;\n if (!enabled) continue;\n\n if (server.auth === 'dcr') {\n if (options?.dcrAuth) {\n try {\n const { token } =\n await options.dcrAuth.authService.getPluginRequestToken({\n onBehalfOf: options.dcrAuth.credentials,\n targetPluginId: 'mcp-actions',\n });\n headers[server.name] = { Authorization: `${token}` };\n } catch (err) {\n options?.logger?.error(\n `Failed to mint DCR token for MCP server '${server.name}': ${err}`,\n );\n }\n } else {\n options?.logger?.warn(\n `MCP server '${server.name}' has auth: dcr but AuthService is not available; skipping`,\n );\n }\n } else {\n // Static token: user's personal token (DB) > admin default (app-config).\n // If neither exists, the server is excluded from MCP-HEADERS.\n const token = setting?.token || server.token;\n if (token) {\n headers[server.name] = { Authorization: `${token}` };\n }\n }\n }\n\n return Object.keys(headers).length > 0 ? JSON.stringify(headers) : '';\n}\n\n/**\n * @public\n * The lightspeed backend router\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, database, httpAuth, auth, userInfo, permissions } =\n options;\n\n const router = Router();\n router.use(express.json());\n\n const port =\n config.getOptionalNumber('intelligent-assistant.servicePort') ??\n DEFAULT_LIGHTSPEED_SERVICE_PORT;\n const system_prompt = config.getOptionalString(\n 'intelligent-assistant.systemPrompt',\n );\n const lcsBaseUrl = `http://${DEFAULT_LIGHTSPEED_SERVICE_HOST}:${port}`;\n\n const apiProxy = createProxyMiddleware({\n target: lcsBaseUrl,\n changeOrigin: true,\n pathRewrite: (path, req) => {\n const userEntityRef = (req as any).userEntityRef;\n const newPath = rewriteProxyPath(path, userEntityRef);\n\n if (newPath !== path) {\n logger.info(`Rewriting path from ${path} to ${newPath}`);\n }\n return newPath;\n },\n });\n\n const vectorStoresOperator = VectorStoresOperator.getInstance(\n lcsBaseUrl,\n logger,\n );\n // Parse admin-configured MCP servers from app-config.\n //\n // Each entry supports:\n // name (required) — unique identifier, must match the name in lightspeed-stack.yaml\n // auth (optional) — authentication mode:\n // 'dcr' = the backend mints a per-user Backstage token (for\n // Backstage-internal MCP servers with DCR/OAuth enabled)\n // absent = legacy static-token mode (see `token` below)\n // token (optional) — static fallback token from app-config. Ignored when auth: dcr.\n // Users can also set personal tokens via the Lightspeed UI.\n //\n // URLs come from LCS (GET /v1/mcp-servers), not from app-config.\n const mcpServersConfig = config.getOptionalConfigArray(\n 'intelligent-assistant.mcpServers',\n );\n const staticServers: StaticMcpServer[] = [];\n if (mcpServersConfig) {\n for (const mcpServer of mcpServersConfig) {\n const authValue = mcpServer.getOptionalString('auth')?.toLowerCase();\n if (authValue && authValue !== 'dcr') {\n logger.warn(\n `MCP server '${mcpServer.getString('name')}' has unsupported auth value '${authValue}'; ` +\n `only 'dcr' is supported — falling back to static-token mode`,\n );\n }\n staticServers.push({\n name: mcpServer.getString('name'),\n token: mcpServer.getOptionalString('token'),\n auth: authValue === 'dcr' ? 'dcr' : undefined,\n });\n }\n }\n\n // Initialize database-backed store for per-user preferences and validator\n const dbClient = await database.getClient();\n const encryptor = createTokenEncryptor(config, logger);\n const settingsStore = new McpUserSettingsStore(dbClient, encryptor, logger);\n const mcpValidator = new McpServerValidator(logger);\n\n // URL cache populated from LCS GET /v1/mcp-servers.\n // The canonical URL for each MCP server lives in LCS config, not app-config.\n const lcsUrlCache = new Map<string, string>();\n\n async function refreshLcsUrlCache(): Promise<void> {\n try {\n const response = await fetch(`${lcsBaseUrl}/v1/mcp-servers`, {\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) {\n logger.warn(\n `Failed to fetch MCP server URLs from LCS: HTTP ${response.status}`,\n );\n return;\n }\n const data = (await response.json()) as {\n servers: Array<{ name: string; url: string }>;\n };\n for (const s of data.servers) {\n lcsUrlCache.set(s.name, s.url);\n }\n logger.info(`Cached ${lcsUrlCache.size} MCP server URL(s) from LCS`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.warn(`Failed to fetch MCP server URLs from LCS: ${msg}`);\n }\n }\n\n function resolveServerUrl(serverName: string): string | undefined {\n return lcsUrlCache.get(serverName);\n }\n\n // Best-effort URL cache on startup (non-blocking)\n refreshLcsUrlCache().catch(() => {});\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n const permissionIntegrationRouter = createPermissionIntegrationRouter({\n permissions: iaPermissions,\n });\n router.use(permissionIntegrationRouter);\n\n const identityMiddleware = createIdentityMiddleware(\n httpAuth,\n userInfo,\n logger,\n );\n router.use((req, res, next) => {\n // Middleware mounts to notebooks router independently\n // so we skip it here\n if (req.path.startsWith('/notebooks')) {\n return next();\n }\n return identityMiddleware(req, res, next);\n });\n\n const authorizer = userPermissionAuthorization(permissions);\n\n function requirePermission(permission: BasicPermission) {\n return createPermissionMiddleware(authorizer, permission, logger);\n }\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 // ─── MCP Server Management Endpoints ────────────────────────────────\n // All MCP servers are admin-configured (static). Users can view the\n // list, toggle servers on/off, and provide personal access tokens.\n\n router.get(\n '/mcp-servers',\n generalRateLimiter,\n requirePermission(iaMcpUsePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const userSettings = await settingsStore.listByUser(userEntityRef);\n const settingsMap = new Map(userSettings.map(s => [s.server_name, s]));\n\n const hasAllUrls = staticServers.every(s => lcsUrlCache.has(s.name));\n if (!hasAllUrls) {\n await refreshLcsUrlCache();\n }\n\n const servers: McpServerResponse[] = staticServers.map(server => {\n const setting = settingsMap.get(server.name);\n return {\n name: server.name,\n url: resolveServerUrl(server.name),\n enabled: setting ? Boolean(setting.enabled) : true,\n status: setting?.status ?? 'unknown',\n toolCount: setting?.tool_count ?? 0,\n // DCR servers always have a token (minted per-request); static servers check DB + config.\n hasToken:\n server.auth === 'dcr' || !!(setting?.token || server.token),\n hasUserToken: !!setting?.token,\n hasOrgToken: server.auth !== 'dcr' && !!server.token,\n auth: server.auth,\n };\n });\n\n res.json({ servers });\n } catch (error) {\n logger.error(`Error listing MCP servers: ${error}`);\n res.status(500).json({ error: 'Failed to list MCP servers' });\n }\n },\n );\n\n router.post(\n '/mcp-servers/validate',\n generalRateLimiter,\n requirePermission(iaMcpUsePermission),\n async (req, res) => {\n try {\n const { url, token } = req.body;\n if (!url || !token) {\n res.status(400).json({ error: 'url and token are required' });\n return;\n }\n\n // SSRF protection: only allow URLs registered in LCS\n let knownUrls = new Set(lcsUrlCache.values());\n if (!knownUrls.has(url)) {\n await refreshLcsUrlCache();\n knownUrls = new Set(lcsUrlCache.values());\n }\n if (!knownUrls.has(url)) {\n res.status(400).json({\n error:\n 'URL not recognized — only MCP server URLs registered in LCS are allowed',\n });\n return;\n }\n\n const result = await mcpValidator.validate(url, token);\n res.json(result);\n } catch (error) {\n logger.error(`Error validating MCP credentials: ${error}`);\n res.status(500).json({ error: 'Validation failed' });\n }\n },\n );\n\n router.post(\n '/mcp-servers/:name/validate',\n generalRateLimiter,\n requirePermission(iaMcpManagePermission),\n async (req, res) => {\n try {\n const { userEntityRef, credentials } = getIdentity(req);\n\n const { name } = req.params;\n const server = staticServers.find(s => s.name === name);\n if (!server) {\n res.status(404).json({\n error: `MCP server '${name}' is not configured — it must be defined in the Lightspeed Stack config and listed under intelligent-assistant.mcpServers in app-config`,\n });\n return;\n }\n // Resolve URL: LCS cache → fresh LCS fetch\n let serverUrl = resolveServerUrl(server.name);\n if (!serverUrl) {\n await refreshLcsUrlCache();\n serverUrl = resolveServerUrl(server.name);\n }\n if (!serverUrl) {\n res\n .status(400)\n .json({ error: 'Server has no URL — not found in LCS or config' });\n return;\n }\n\n let effectiveToken: string | undefined;\n if (server.auth === 'dcr') {\n try {\n const { token: dcrToken } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'mcp-actions',\n });\n effectiveToken = dcrToken;\n } catch (err) {\n logger.error(\n `Failed to mint DCR token for server '${name}': ${err}`,\n );\n res.status(502).json({\n error: `Failed to mint authentication token for DCR server '${name}' — check Backstage auth configuration`,\n });\n return;\n }\n } else {\n const setting = await settingsStore.get(name, userEntityRef);\n effectiveToken = setting?.token || server.token;\n }\n if (!effectiveToken) {\n res\n .status(400)\n .json({ error: 'No token available — provide one first' });\n return;\n }\n\n const validation = await mcpValidator.validate(\n serverUrl,\n effectiveToken,\n );\n const status: McpServerStatus = validation.valid\n ? 'connected'\n : 'error';\n\n await settingsStore.updateStatus(\n name,\n userEntityRef,\n status,\n validation.toolCount,\n );\n\n res.json({\n name,\n status,\n toolCount: validation.toolCount,\n validation,\n });\n } catch (error) {\n logger.error(`Error validating MCP server: ${error}`);\n res.status(500).json({ error: 'Validation failed' });\n }\n },\n );\n\n router.patch(\n '/mcp-servers/:name',\n generalRateLimiter,\n requirePermission(iaMcpManagePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const { name } = req.params;\n const server = staticServers.find(s => s.name === name);\n if (!server) {\n res.status(404).json({\n error: `MCP server '${name}' is not configured — it must be defined in the Lightspeed Stack config and listed under intelligent-assistant.mcpServers in app-config`,\n });\n return;\n }\n\n const body = (req.body ?? {}) as {\n enabled?: boolean;\n token?: string | null;\n };\n const hasEnabledField = Object.prototype.hasOwnProperty.call(\n body,\n 'enabled',\n );\n const hasTokenField = Object.prototype.hasOwnProperty.call(\n body,\n 'token',\n );\n const { enabled, token } = body;\n if (!hasEnabledField && !hasTokenField) {\n res.status(400).json({\n error: 'At least one of enabled or token must be provided',\n });\n return;\n }\n\n const setting = await settingsStore.upsert(name, userEntityRef, {\n enabled: hasEnabledField ? enabled : undefined,\n token: hasTokenField ? token : undefined,\n });\n\n let validation: McpValidationResult | undefined;\n let serverUrl = resolveServerUrl(server.name);\n if (token && !serverUrl) {\n await refreshLcsUrlCache();\n serverUrl = resolveServerUrl(server.name);\n }\n if (token && serverUrl) {\n validation = await mcpValidator.validate(serverUrl, token);\n const newStatus: McpServerStatus = validation.valid\n ? 'connected'\n : 'error';\n await settingsStore.updateStatus(\n name,\n userEntityRef,\n newStatus,\n validation.toolCount,\n );\n setting.status = newStatus;\n setting.tool_count = validation.toolCount;\n }\n\n const result: Record<string, unknown> = {\n server: {\n name: server.name,\n url: resolveServerUrl(server.name),\n enabled: Boolean(setting.enabled),\n status: setting.status,\n toolCount: setting.tool_count,\n hasToken:\n server.auth === 'dcr' || !!(setting.token || server.token),\n hasUserToken: !!setting.token,\n hasOrgToken: server.auth !== 'dcr' && !!server.token,\n auth: server.auth,\n },\n };\n if (validation) result.validation = validation;\n res.json(result);\n } catch (error) {\n logger.error(`Error updating MCP server settings: ${error}`);\n res.status(500).json({ error: 'Failed to update MCP server settings' });\n }\n },\n );\n\n // Returns conversation IDs associated with notebook sessions for filtering\n router.get(\n '/notebook-conversation-ids',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const vectorStoresPage = await vectorStoresOperator.vectorStores.list();\n const vectorStores = vectorStoresPage.data || [];\n\n const conversationIds: string[] = [];\n\n for (const store of vectorStores) {\n const sessionUserId = store.metadata?.user_id as string;\n const conversationId = store.metadata?.conversation_id as\n string | null;\n\n // Only include this user's sessions with a conversation_id\n if (sessionUserId === userEntityRef && conversationId) {\n conversationIds.push(conversationId);\n }\n }\n\n res.json({\n conversation_ids: conversationIds,\n });\n } catch (error) {\n const errormsg = `Error fetching notebook conversation IDs: ${error}`;\n logger.error(errormsg);\n\n if (error instanceof NotAllowedError) {\n res.status(403).json({ error: error.message });\n } else {\n res.status(500).json({ error: errormsg });\n }\n }\n },\n );\n\n // ─── Proxy Routes ───────────────────────────────────────────────────\n\n router.get(\n '/v1/models',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v1/shields',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.delete(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatManagePermission),\n apiProxy,\n );\n router.get(\n '/v1/feedback/status',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n\n router.get(\n '/v1/saved-prompts/config',\n generalRateLimiter,\n requirePermission(iaSavedPromptsManagePermission),\n apiProxy, // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint\n );\n router.get(\n '/v1/saved-prompts',\n generalRateLimiter,\n requirePermission(iaSavedPromptsManagePermission),\n apiProxy,\n );\n router.delete(\n '/v1/saved-prompts/:prompt_id',\n generalRateLimiter,\n requirePermission(iaSavedPromptsManagePermission),\n apiProxy,\n );\n\n router.get(\n '/v1/skills',\n generalRateLimiter,\n requirePermission(iaSkillsAccessPermission),\n apiProxy,\n );\n\n router.post(\n '/v1/feedback',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n\n logger.info(`/v1/feedback receives call from user: ${userEntityRef}`);\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/feedback?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'sending feedback',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while sending feedback: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/saved-prompts',\n generalRateLimiter,\n requirePermission(iaSavedPromptsManagePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n\n logger.info(\n `/v1/saved-prompts receives call from user: ${userEntityRef}`,\n );\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/saved-prompts?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'creating saved prompt',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while creating saved prompt: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/query/interrupt',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/streaming_query/interrupt?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'interrupting query',\n response,\n );\n return;\n }\n response.status(fetchResponse.status).json(await fetchResponse.json());\n } catch (error) {\n const errormsg = `Error while interrupting query: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/query',\n express.json({ limit: EXPRESS_JSON_BODY_LIMIT }),\n expensiveRateLimiter,\n validateCompletionsRequest,\n validateAttachmentsForModel,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n const { provider }: Pick<QueryRequestBody, 'provider'> = request.body;\n try {\n const { userEntityRef, credentials } = getIdentity(request);\n logger.info(`/v1/query receives call from user: ${userEntityRef}`);\n if (request.body.attachments?.length) {\n logger.info(\n `/v1/query includes ${request.body.attachments.length} attachment(s): ${request.body.attachments.map((a: { attachment_type: string }) => a.attachment_type).join(', ')}`,\n );\n }\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n request.body.media_type = 'application/json'; // set media_type to receive start and end event\n // if system_prompt is defined in lightspeed config\n // set system_prompt to override the default rhdh system prompt\n if (system_prompt && system_prompt.trim().length > 0) {\n request.body.system_prompt = system_prompt;\n }\n\n const requestBody = JSON.stringify(request.body);\n\n // Build MCP headers from config servers + this user's preferences.\n // For servers with auth: dcr, a per-user Backstage token is minted.\n const mcpHeadersValue = await buildMcpHeaders(\n staticServers,\n settingsStore,\n userEntityRef,\n { dcrAuth: { authService: auth, credentials }, logger },\n );\n\n const abortController = new AbortController();\n\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/streaming_query?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'MCP-HEADERS': mcpHeadersValue,\n },\n body: requestBody,\n signal: abortController.signal,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'processing query',\n response,\n );\n return;\n }\n\n // Pipe the response back to the original response\n if (fetchResponse.body) {\n const nodeStream = Readable.fromWeb(fetchResponse.body as any);\n\n nodeStream.on('error', (error: Error) => {\n logger.error(\n `Upstream stream error while processing query: ${error}`,\n );\n if (response.headersSent) {\n response.destroy();\n } else {\n response.status(500).json({ error: 'Stream error occurred' });\n }\n abortController.abort();\n });\n\n response.on('close', () => {\n if (!response.writableFinished) {\n logger.warn('Client disconnected while processing query');\n nodeStream.destroy();\n abortController.abort();\n }\n });\n\n nodeStream.pipe(response);\n }\n } catch (error) {\n const errormsg = `Error fetching completions from ${provider}: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.put(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatManagePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n const conversation_id = request.params.conversation_id;\n\n const requestBody = JSON.stringify(request.body);\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v2/conversations/${conversation_id}?${userQueryParam}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'updating conversation',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while updating topic summary: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/validate-model-vision',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n const { model, provider } = request.body;\n\n if (\n typeof model !== 'string' ||\n !model.trim() ||\n typeof provider !== 'string' ||\n !provider.trim()\n ) {\n response.status(400).json({ error: 'model and provider are required' });\n return;\n }\n\n const cacheKey = `${provider}/${model}`;\n\n try {\n logger.info(`Vision validation requested for model: ${cacheKey}`);\n\n if (ModelCapabilitiesCache.has(cacheKey)) {\n const cached = ModelCapabilitiesCache.get(cacheKey)!;\n logger.info(`Cache hit for ${cacheKey}: ${cached}`);\n response.json({ model, provider, supportsVision: cached });\n return;\n }\n\n const testJpeg = `data:image/jpeg;base64,${TEST_VISION_JPEG}`;\n const testResponse = await fetch(`${lcsBaseUrl}/v1/responses`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: cacheKey,\n input: [\n {\n type: 'message',\n role: 'user',\n content: [\n {\n type: 'input_image',\n image_url: testJpeg,\n detail: 'low',\n },\n {\n type: 'input_text',\n text: 'hi, respond with hi.',\n },\n ],\n },\n ],\n tool_choice: 'none',\n temperature: 0,\n store: false,\n stream: false,\n }),\n });\n\n if (testResponse.ok) {\n ModelCapabilitiesCache.set(cacheKey, true);\n response.json({ model, provider, supportsVision: true });\n }\n } catch (error) {\n logger.error(`Vision test error for ${cacheKey}:`, error);\n response.status(502).json({\n error:\n 'Unable to verify vision support for model — upstream unreachable',\n model,\n provider,\n });\n }\n },\n );\n\n router.use((_request, response) => {\n response.status(404).json({ error: 'Requested path is not available' });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n}\n"],"names":["Router","express","DEFAULT_LIGHTSPEED_SERVICE_PORT","DEFAULT_LIGHTSPEED_SERVICE_HOST","createProxyMiddleware","rewriteProxyPath","VectorStoresOperator","createTokenEncryptor","McpUserSettingsStore","McpServerValidator","createPermissionIntegrationRouter","iaPermissions","createIdentityMiddleware","userPermissionAuthorization","createPermissionMiddleware","createRateLimitMiddleware","iaMcpUsePermission","getIdentity","iaMcpManagePermission","iaNotebooksUsePermission","NotAllowedError","iaChatAccessPermission","iaChatManagePermission","iaSavedPromptsManagePermission","iaSkillsAccessPermission","iaChatUsePermission","handleLCSFetchError","EXPRESS_JSON_BODY_LIMIT","validateCompletionsRequest","validateAttachmentsForModel","Readable","ModelCapabilitiesCache","TEST_VISION_JPEG","MiddlewareFactory"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.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 { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport type {\n AuthService,\n BackstageCredentials,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport type { BasicPermission } from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\n\nimport express, { Router } from 'express';\nimport { createProxyMiddleware } from 'http-proxy-middleware';\n\nimport {\n iaChatAccessPermission,\n iaChatManagePermission,\n iaChatUsePermission,\n iaMcpManagePermission,\n iaMcpUsePermission,\n iaNotebooksUsePermission,\n iaPermissions,\n iaSkillsAccessPermission,\n} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { Readable } from 'node:stream';\n\nimport { ModelCapabilitiesCache } from './attachment-validation';\nimport {\n DEFAULT_LIGHTSPEED_SERVICE_HOST,\n DEFAULT_LIGHTSPEED_SERVICE_PORT,\n EXPRESS_JSON_BODY_LIMIT,\n TEST_VISION_JPEG,\n} from './constant';\nimport { McpUserSettingsStore } from './mcp-server-store';\nimport {\n McpServerAuth,\n McpServerResponse,\n McpServerStatus,\n McpValidationResult,\n} from './mcp-server-types';\nimport { McpServerValidator } from './mcp-server-validator';\nimport { createPermissionMiddleware } from './middleware/createPermissionMiddleware';\nimport { createRateLimitMiddleware } from './middleware/createRateLimitMiddleware';\nimport {\n createIdentityMiddleware,\n getIdentity,\n} from './middleware/getIdentity';\nimport { VectorStoresOperator } from './notebooks/VectorStoresOperator';\nimport { userPermissionAuthorization } from './permission';\nimport { createTokenEncryptor } from './token-encryption';\nimport { QueryRequestBody, RouterOptions } from './types';\nimport { handleLCSFetchError, rewriteProxyPath } from './utils';\nimport {\n validateAttachmentsForModel,\n validateCompletionsRequest,\n} from './validation';\n\n/**\n * MCP Server authentication modes for lightspeed.mcpServers entries.\n *\n * - `'dcr'` — Dynamic Client Registration. The Lightspeed backend mints a\n * short-lived Backstage plugin-request token carrying the current user's\n * identity. This token is forwarded to the MCP server via LCORE's\n * MCP-HEADERS, and the MCP Actions backend validates it against the\n * Backstage Permission Framework (RBAC). Use this for Backstage-internal\n * MCP servers (e.g. `@backstage/plugin-mcp-actions-backend`).\n * A static `token` field is **ignored** when `auth: dcr` is set.\n *\n * - (absent / undefined) — Legacy static-token mode. The token is resolved\n * from the user's DB override first, then from the `token` field in\n * app-config. If neither exists, the server is omitted from MCP-HEADERS.\n * Users can also set their own tokens via the Lightspeed UI.\n */\ninterface StaticMcpServer {\n name: string;\n token?: string;\n auth?: McpServerAuth;\n}\n\n/**\n * Build MCP-HEADERS for LCS. Format matches the LCS \"client\"/\"oauth\" auth\n * model: `{ \"server-name\": { \"Authorization\": \"<token>\" } }`\n *\n * For each admin-configured server:\n *\n * 1. If `auth: 'dcr'`, a Backstage plugin-request token is minted on behalf\n * of the authenticated user. Any static token is ignored.\n *\n * 2. Otherwise, the user's personal token (DB) takes precedence over the\n * admin default from app-config. If neither exists, the server is\n * excluded from MCP-HEADERS (LCORE will skip it or return 401).\n *\n * Servers the user has disabled are always excluded.\n */\nasync function buildMcpHeaders(\n servers: StaticMcpServer[],\n store: McpUserSettingsStore,\n userEntityRef: string,\n options?: {\n dcrAuth?: { authService: AuthService; credentials: BackstageCredentials };\n logger?: LoggerService;\n },\n): Promise<string> {\n const headers: Record<string, { Authorization: string }> = {};\n const userSettings = await store.listByUser(userEntityRef);\n const settingsMap = new Map(userSettings.map(s => [s.server_name, s]));\n\n for (const server of servers) {\n const setting = settingsMap.get(server.name);\n const enabled = setting ? Boolean(setting.enabled) : true;\n if (!enabled) continue;\n\n if (server.auth === 'dcr') {\n if (options?.dcrAuth) {\n try {\n const { token } =\n await options.dcrAuth.authService.getPluginRequestToken({\n onBehalfOf: options.dcrAuth.credentials,\n targetPluginId: 'mcp-actions',\n });\n headers[server.name] = { Authorization: `${token}` };\n } catch (err) {\n options?.logger?.error(\n `Failed to mint DCR token for MCP server '${server.name}': ${err}`,\n );\n }\n } else {\n options?.logger?.warn(\n `MCP server '${server.name}' has auth: dcr but AuthService is not available; skipping`,\n );\n }\n } else {\n // Static token: user's personal token (DB) > admin default (app-config).\n // If neither exists, the server is excluded from MCP-HEADERS.\n const token = setting?.token || server.token;\n if (token) {\n headers[server.name] = { Authorization: `${token}` };\n }\n }\n }\n\n return Object.keys(headers).length > 0 ? JSON.stringify(headers) : '';\n}\n\n/**\n * @public\n * The lightspeed backend router\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, database, httpAuth, auth, userInfo, permissions } =\n options;\n\n const router = Router();\n router.use(express.json());\n\n const port =\n config.getOptionalNumber('intelligent-assistant.servicePort') ??\n DEFAULT_LIGHTSPEED_SERVICE_PORT;\n const system_prompt = config.getOptionalString(\n 'intelligent-assistant.systemPrompt',\n );\n const lcsBaseUrl = `http://${DEFAULT_LIGHTSPEED_SERVICE_HOST}:${port}`;\n\n const apiProxy = createProxyMiddleware({\n target: lcsBaseUrl,\n changeOrigin: true,\n pathRewrite: (path, req) => {\n const userEntityRef = (req as any).userEntityRef;\n const newPath = rewriteProxyPath(path, userEntityRef);\n\n if (newPath !== path) {\n logger.info(`Rewriting path from ${path} to ${newPath}`);\n }\n return newPath;\n },\n });\n\n const vectorStoresOperator = VectorStoresOperator.getInstance(\n lcsBaseUrl,\n logger,\n );\n // Parse admin-configured MCP servers from app-config.\n //\n // Each entry supports:\n // name (required) — unique identifier, must match the name in lightspeed-stack.yaml\n // auth (optional) — authentication mode:\n // 'dcr' = the backend mints a per-user Backstage token (for\n // Backstage-internal MCP servers with DCR/OAuth enabled)\n // absent = legacy static-token mode (see `token` below)\n // token (optional) — static fallback token from app-config. Ignored when auth: dcr.\n // Users can also set personal tokens via the Lightspeed UI.\n //\n // URLs come from LCS (GET /v1/mcp-servers), not from app-config.\n const mcpServersConfig = config.getOptionalConfigArray(\n 'intelligent-assistant.mcpServers',\n );\n const staticServers: StaticMcpServer[] = [];\n if (mcpServersConfig) {\n for (const mcpServer of mcpServersConfig) {\n const authValue = mcpServer.getOptionalString('auth')?.toLowerCase();\n if (authValue && authValue !== 'dcr') {\n logger.warn(\n `MCP server '${mcpServer.getString('name')}' has unsupported auth value '${authValue}'; ` +\n `only 'dcr' is supported — falling back to static-token mode`,\n );\n }\n staticServers.push({\n name: mcpServer.getString('name'),\n token: mcpServer.getOptionalString('token'),\n auth: authValue === 'dcr' ? 'dcr' : undefined,\n });\n }\n }\n\n // Initialize database-backed store for per-user preferences and validator\n const dbClient = await database.getClient();\n const encryptor = createTokenEncryptor(config, logger);\n const settingsStore = new McpUserSettingsStore(dbClient, encryptor, logger);\n const mcpValidator = new McpServerValidator(logger);\n\n // URL cache populated from LCS GET /v1/mcp-servers.\n // The canonical URL for each MCP server lives in LCS config, not app-config.\n const lcsUrlCache = new Map<string, string>();\n\n async function refreshLcsUrlCache(): Promise<void> {\n try {\n const response = await fetch(`${lcsBaseUrl}/v1/mcp-servers`, {\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) {\n logger.warn(\n `Failed to fetch MCP server URLs from LCS: HTTP ${response.status}`,\n );\n return;\n }\n const data = (await response.json()) as {\n servers: Array<{ name: string; url: string }>;\n };\n for (const s of data.servers) {\n lcsUrlCache.set(s.name, s.url);\n }\n logger.info(`Cached ${lcsUrlCache.size} MCP server URL(s) from LCS`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.warn(`Failed to fetch MCP server URLs from LCS: ${msg}`);\n }\n }\n\n function resolveServerUrl(serverName: string): string | undefined {\n return lcsUrlCache.get(serverName);\n }\n\n // Best-effort URL cache on startup (non-blocking)\n refreshLcsUrlCache().catch(() => {});\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n const permissionIntegrationRouter = createPermissionIntegrationRouter({\n permissions: iaPermissions,\n });\n router.use(permissionIntegrationRouter);\n\n const identityMiddleware = createIdentityMiddleware(\n httpAuth,\n userInfo,\n logger,\n );\n router.use((req, res, next) => {\n // Middleware mounts to notebooks router independently\n // so we skip it here\n if (req.path.startsWith('/notebooks')) {\n return next();\n }\n return identityMiddleware(req, res, next);\n });\n\n const authorizer = userPermissionAuthorization(permissions);\n\n function requirePermission(permission: BasicPermission) {\n return createPermissionMiddleware(authorizer, permission, logger);\n }\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 // ─── MCP Server Management Endpoints ────────────────────────────────\n // All MCP servers are admin-configured (static). Users can view the\n // list, toggle servers on/off, and provide personal access tokens.\n\n router.get(\n '/mcp-servers',\n generalRateLimiter,\n requirePermission(iaMcpUsePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const userSettings = await settingsStore.listByUser(userEntityRef);\n const settingsMap = new Map(userSettings.map(s => [s.server_name, s]));\n\n const hasAllUrls = staticServers.every(s => lcsUrlCache.has(s.name));\n if (!hasAllUrls) {\n await refreshLcsUrlCache();\n }\n\n const servers: McpServerResponse[] = staticServers.map(server => {\n const setting = settingsMap.get(server.name);\n return {\n name: server.name,\n url: resolveServerUrl(server.name),\n enabled: setting ? Boolean(setting.enabled) : true,\n status: setting?.status ?? 'unknown',\n toolCount: setting?.tool_count ?? 0,\n // DCR servers always have a token (minted per-request); static servers check DB + config.\n hasToken:\n server.auth === 'dcr' || !!(setting?.token || server.token),\n hasUserToken: !!setting?.token,\n hasOrgToken: server.auth !== 'dcr' && !!server.token,\n auth: server.auth,\n };\n });\n\n res.json({ servers });\n } catch (error) {\n logger.error(`Error listing MCP servers: ${error}`);\n res.status(500).json({ error: 'Failed to list MCP servers' });\n }\n },\n );\n\n router.post(\n '/mcp-servers/validate',\n generalRateLimiter,\n requirePermission(iaMcpUsePermission),\n async (req, res) => {\n try {\n const { url, token } = req.body;\n if (!url || !token) {\n res.status(400).json({ error: 'url and token are required' });\n return;\n }\n\n // SSRF protection: only allow URLs registered in LCS\n let knownUrls = new Set(lcsUrlCache.values());\n if (!knownUrls.has(url)) {\n await refreshLcsUrlCache();\n knownUrls = new Set(lcsUrlCache.values());\n }\n if (!knownUrls.has(url)) {\n res.status(400).json({\n error:\n 'URL not recognized — only MCP server URLs registered in LCS are allowed',\n });\n return;\n }\n\n const result = await mcpValidator.validate(url, token);\n res.json(result);\n } catch (error) {\n logger.error(`Error validating MCP credentials: ${error}`);\n res.status(500).json({ error: 'Validation failed' });\n }\n },\n );\n\n router.post(\n '/mcp-servers/:name/validate',\n generalRateLimiter,\n requirePermission(iaMcpManagePermission),\n async (req, res) => {\n try {\n const { userEntityRef, credentials } = getIdentity(req);\n\n const { name } = req.params;\n const server = staticServers.find(s => s.name === name);\n if (!server) {\n res.status(404).json({\n error: `MCP server '${name}' is not configured — it must be defined in the Lightspeed Stack config and listed under intelligent-assistant.mcpServers in app-config`,\n });\n return;\n }\n // Resolve URL: LCS cache → fresh LCS fetch\n let serverUrl = resolveServerUrl(server.name);\n if (!serverUrl) {\n await refreshLcsUrlCache();\n serverUrl = resolveServerUrl(server.name);\n }\n if (!serverUrl) {\n res\n .status(400)\n .json({ error: 'Server has no URL — not found in LCS or config' });\n return;\n }\n\n let effectiveToken: string | undefined;\n if (server.auth === 'dcr') {\n try {\n const { token: dcrToken } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'mcp-actions',\n });\n effectiveToken = dcrToken;\n } catch (err) {\n logger.error(\n `Failed to mint DCR token for server '${name}': ${err}`,\n );\n res.status(502).json({\n error: `Failed to mint authentication token for DCR server '${name}' — check Backstage auth configuration`,\n });\n return;\n }\n } else {\n const setting = await settingsStore.get(name, userEntityRef);\n effectiveToken = setting?.token || server.token;\n }\n if (!effectiveToken) {\n res\n .status(400)\n .json({ error: 'No token available — provide one first' });\n return;\n }\n\n const validation = await mcpValidator.validate(\n serverUrl,\n effectiveToken,\n );\n const status: McpServerStatus = validation.valid\n ? 'connected'\n : 'error';\n\n await settingsStore.updateStatus(\n name,\n userEntityRef,\n status,\n validation.toolCount,\n );\n\n res.json({\n name,\n status,\n toolCount: validation.toolCount,\n validation,\n });\n } catch (error) {\n logger.error(`Error validating MCP server: ${error}`);\n res.status(500).json({ error: 'Validation failed' });\n }\n },\n );\n\n router.patch(\n '/mcp-servers/:name',\n generalRateLimiter,\n requirePermission(iaMcpManagePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const { name } = req.params;\n const server = staticServers.find(s => s.name === name);\n if (!server) {\n res.status(404).json({\n error: `MCP server '${name}' is not configured — it must be defined in the Lightspeed Stack config and listed under intelligent-assistant.mcpServers in app-config`,\n });\n return;\n }\n\n const body = (req.body ?? {}) as {\n enabled?: boolean;\n token?: string | null;\n };\n const hasEnabledField = Object.prototype.hasOwnProperty.call(\n body,\n 'enabled',\n );\n const hasTokenField = Object.prototype.hasOwnProperty.call(\n body,\n 'token',\n );\n const { enabled, token } = body;\n if (!hasEnabledField && !hasTokenField) {\n res.status(400).json({\n error: 'At least one of enabled or token must be provided',\n });\n return;\n }\n\n const setting = await settingsStore.upsert(name, userEntityRef, {\n enabled: hasEnabledField ? enabled : undefined,\n token: hasTokenField ? token : undefined,\n });\n\n let validation: McpValidationResult | undefined;\n let serverUrl = resolveServerUrl(server.name);\n if (token && !serverUrl) {\n await refreshLcsUrlCache();\n serverUrl = resolveServerUrl(server.name);\n }\n if (token && serverUrl) {\n validation = await mcpValidator.validate(serverUrl, token);\n const newStatus: McpServerStatus = validation.valid\n ? 'connected'\n : 'error';\n await settingsStore.updateStatus(\n name,\n userEntityRef,\n newStatus,\n validation.toolCount,\n );\n setting.status = newStatus;\n setting.tool_count = validation.toolCount;\n }\n\n const result: Record<string, unknown> = {\n server: {\n name: server.name,\n url: resolveServerUrl(server.name),\n enabled: Boolean(setting.enabled),\n status: setting.status,\n toolCount: setting.tool_count,\n hasToken:\n server.auth === 'dcr' || !!(setting.token || server.token),\n hasUserToken: !!setting.token,\n hasOrgToken: server.auth !== 'dcr' && !!server.token,\n auth: server.auth,\n },\n };\n if (validation) result.validation = validation;\n res.json(result);\n } catch (error) {\n logger.error(`Error updating MCP server settings: ${error}`);\n res.status(500).json({ error: 'Failed to update MCP server settings' });\n }\n },\n );\n\n // Returns conversation IDs associated with notebook sessions for filtering\n router.get(\n '/notebook-conversation-ids',\n generalRateLimiter,\n requirePermission(iaNotebooksUsePermission),\n async (req, res) => {\n try {\n const { userEntityRef } = getIdentity(req);\n\n const vectorStoresPage = await vectorStoresOperator.vectorStores.list();\n const vectorStores = vectorStoresPage.data || [];\n\n const conversationIds: string[] = [];\n\n for (const store of vectorStores) {\n const sessionUserId = store.metadata?.user_id as string;\n const conversationId = store.metadata?.conversation_id as\n string | null;\n\n // Only include this user's sessions with a conversation_id\n if (sessionUserId === userEntityRef && conversationId) {\n conversationIds.push(conversationId);\n }\n }\n\n res.json({\n conversation_ids: conversationIds,\n });\n } catch (error) {\n const errormsg = `Error fetching notebook conversation IDs: ${error}`;\n logger.error(errormsg);\n\n if (error instanceof NotAllowedError) {\n res.status(403).json({ error: error.message });\n } else {\n res.status(500).json({ error: errormsg });\n }\n }\n },\n );\n\n // ─── Proxy Routes ───────────────────────────────────────────────────\n\n router.get(\n '/v1/models',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v1/shields',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n router.delete(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatManagePermission),\n apiProxy,\n );\n router.get(\n '/v1/feedback/status',\n generalRateLimiter,\n requirePermission(iaChatAccessPermission),\n apiProxy,\n );\n\n router.get(\n '/v1/saved-prompts/config',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n apiProxy, // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint\n );\n router.get(\n '/v1/saved-prompts',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n apiProxy,\n );\n router.delete(\n '/v1/saved-prompts/:prompt_id',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n apiProxy,\n );\n\n router.get(\n '/v1/skills',\n generalRateLimiter,\n requirePermission(iaSkillsAccessPermission),\n apiProxy,\n );\n\n router.post(\n '/v1/feedback',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n\n logger.info(`/v1/feedback receives call from user: ${userEntityRef}`);\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/feedback?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'sending feedback',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while sending feedback: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/saved-prompts',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n\n logger.info(\n `/v1/saved-prompts receives call from user: ${userEntityRef}`,\n );\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/saved-prompts?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'creating saved prompt',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while creating saved prompt: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/query/interrupt',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const requestBody = JSON.stringify(request.body);\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/streaming_query/interrupt?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'interrupting query',\n response,\n );\n return;\n }\n response.status(fetchResponse.status).json(await fetchResponse.json());\n } catch (error) {\n const errormsg = `Error while interrupting query: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/query',\n express.json({ limit: EXPRESS_JSON_BODY_LIMIT }),\n expensiveRateLimiter,\n validateCompletionsRequest,\n validateAttachmentsForModel,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n const { provider }: Pick<QueryRequestBody, 'provider'> = request.body;\n try {\n const { userEntityRef, credentials } = getIdentity(request);\n logger.info(`/v1/query receives call from user: ${userEntityRef}`);\n if (request.body.attachments?.length) {\n logger.info(\n `/v1/query includes ${request.body.attachments.length} attachment(s): ${request.body.attachments.map((a: { attachment_type: string }) => a.attachment_type).join(', ')}`,\n );\n }\n\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n request.body.media_type = 'application/json'; // set media_type to receive start and end event\n // if system_prompt is defined in lightspeed config\n // set system_prompt to override the default rhdh system prompt\n if (system_prompt && system_prompt.trim().length > 0) {\n request.body.system_prompt = system_prompt;\n }\n\n const requestBody = JSON.stringify(request.body);\n\n // Build MCP headers from config servers + this user's preferences.\n // For servers with auth: dcr, a per-user Backstage token is minted.\n const mcpHeadersValue = await buildMcpHeaders(\n staticServers,\n settingsStore,\n userEntityRef,\n { dcrAuth: { authService: auth, credentials }, logger },\n );\n\n const abortController = new AbortController();\n\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v1/streaming_query?${userQueryParam}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'MCP-HEADERS': mcpHeadersValue,\n },\n body: requestBody,\n signal: abortController.signal,\n },\n );\n\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'processing query',\n response,\n );\n return;\n }\n\n // Pipe the response back to the original response\n if (fetchResponse.body) {\n const nodeStream = Readable.fromWeb(fetchResponse.body as any);\n\n nodeStream.on('error', (error: Error) => {\n logger.error(\n `Upstream stream error while processing query: ${error}`,\n );\n if (response.headersSent) {\n response.destroy();\n } else {\n response.status(500).json({ error: 'Stream error occurred' });\n }\n abortController.abort();\n });\n\n response.on('close', () => {\n if (!response.writableFinished) {\n logger.warn('Client disconnected while processing query');\n nodeStream.destroy();\n abortController.abort();\n }\n });\n\n nodeStream.pipe(response);\n }\n } catch (error) {\n const errormsg = `Error fetching completions from ${provider}: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.put(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(iaChatManagePermission),\n async (request, response) => {\n try {\n const { userEntityRef } = getIdentity(request);\n const conversation_id = request.params.conversation_id;\n\n const requestBody = JSON.stringify(request.body);\n const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;\n const fetchResponse = await fetch(\n `${lcsBaseUrl}/v2/conversations/${conversation_id}?${userQueryParam}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: requestBody,\n },\n );\n if (!fetchResponse.ok) {\n await handleLCSFetchError(\n fetchResponse,\n logger,\n 'updating conversation',\n response,\n );\n return;\n }\n\n const data = await fetchResponse.json();\n response.status(fetchResponse.status).json(data);\n } catch (error) {\n const errormsg = `Error while updating topic summary: ${error}`;\n logger.error(errormsg);\n response.status(500).json({ error: errormsg });\n }\n },\n );\n\n router.post(\n '/v1/validate-model-vision',\n generalRateLimiter,\n requirePermission(iaChatUsePermission),\n async (request, response) => {\n const { model, provider } = request.body;\n\n if (\n typeof model !== 'string' ||\n !model.trim() ||\n typeof provider !== 'string' ||\n !provider.trim()\n ) {\n response.status(400).json({ error: 'model and provider are required' });\n return;\n }\n\n const cacheKey = `${provider}/${model}`;\n\n try {\n logger.info(`Vision validation requested for model: ${cacheKey}`);\n\n if (ModelCapabilitiesCache.has(cacheKey)) {\n const cached = ModelCapabilitiesCache.get(cacheKey)!;\n logger.info(`Cache hit for ${cacheKey}: ${cached}`);\n response.json({ model, provider, supportsVision: cached });\n return;\n }\n\n const testJpeg = `data:image/jpeg;base64,${TEST_VISION_JPEG}`;\n const testResponse = await fetch(`${lcsBaseUrl}/v1/responses`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: cacheKey,\n input: [\n {\n type: 'message',\n role: 'user',\n content: [\n {\n type: 'input_image',\n image_url: testJpeg,\n detail: 'low',\n },\n {\n type: 'input_text',\n text: 'hi, respond with hi.',\n },\n ],\n },\n ],\n tool_choice: 'none',\n temperature: 0,\n store: false,\n stream: false,\n }),\n });\n\n if (testResponse.ok) {\n ModelCapabilitiesCache.set(cacheKey, true);\n response.json({ model, provider, supportsVision: true });\n }\n } catch (error) {\n logger.error(`Vision test error for ${cacheKey}:`, error);\n response.status(502).json({\n error:\n 'Unable to verify vision support for model — upstream unreachable',\n model,\n provider,\n });\n }\n },\n );\n\n router.use((_request, response) => {\n response.status(404).json({ error: 'Requested path is not available' });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n}\n"],"names":["Router","express","DEFAULT_LIGHTSPEED_SERVICE_PORT","DEFAULT_LIGHTSPEED_SERVICE_HOST","createProxyMiddleware","rewriteProxyPath","VectorStoresOperator","createTokenEncryptor","McpUserSettingsStore","McpServerValidator","createPermissionIntegrationRouter","iaPermissions","createIdentityMiddleware","userPermissionAuthorization","createPermissionMiddleware","createRateLimitMiddleware","iaMcpUsePermission","getIdentity","iaMcpManagePermission","iaNotebooksUsePermission","NotAllowedError","iaChatAccessPermission","iaChatManagePermission","iaChatUsePermission","iaSkillsAccessPermission","handleLCSFetchError","EXPRESS_JSON_BODY_LIMIT","validateCompletionsRequest","validateAttachmentsForModel","Readable","ModelCapabilitiesCache","TEST_VISION_JPEG","MiddlewareFactory"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "main": "./dist/index.cjs.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -56,18 +56,14 @@
56
56
  "@backstage/errors": "^1.3.1",
57
57
  "@backstage/plugin-permission-common": "^0.9.9",
58
58
  "@backstage/plugin-permission-node": "^0.11.1",
59
- "@langchain/core": "^0.3.80",
60
- "@langchain/openai": "^0.6.0",
61
- "@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common": "^4.0.0",
59
+ "@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common": "^4.1.0",
62
60
  "express": "^4.21.1",
63
61
  "express-rate-limit": "^8.2.2",
64
62
  "form-data": "^4.0.6",
65
- "htmlparser2": "^9.1.0",
66
63
  "http-proxy-middleware": "^3.0.7",
67
- "js-yaml": "^4.1.1",
68
64
  "knex": "^3.1.0",
69
- "multer": "^2.2.0",
70
- "pdfjs-dist": "^4.10.38"
65
+ "markitdown-ts": "^0.0.10",
66
+ "multer": "^2.2.0"
71
67
  },
72
68
  "devDependencies": {
73
69
  "@backstage/backend-test-utils": "^1.11.4",
@@ -75,16 +71,12 @@
75
71
  "@ianvs/prettier-plugin-sort-imports": "^4.4.0",
76
72
  "@spotify/prettier-config": "^15.0.0",
77
73
  "@types/express": "4.17.25",
78
- "@types/js-yaml": "^4",
79
74
  "@types/multer": "^1.4.12",
80
75
  "@types/supertest": "2.0.16",
81
76
  "msw": "2.14.6",
82
77
  "prettier": "3.9.6",
83
78
  "supertest": "6.3.4"
84
79
  },
85
- "resolutions": {
86
- "langsmith": "^0.7.0"
87
- },
88
80
  "files": [
89
81
  "dist",
90
82
  "config.d.ts",
@@ -1,139 +0,0 @@
1
- 'use strict';
2
-
3
- var errors = require('@backstage/errors');
4
- var yaml = require('js-yaml');
5
- var pdfjsLib = require('pdfjs-dist');
6
- var stream = require('stream');
7
- var constant = require('../../constant.cjs.js');
8
-
9
- function _interopNamespaceCompat(e) {
10
- if (e && typeof e === 'object' && 'default' in e) return e;
11
- var n = Object.create(null);
12
- if (e) {
13
- Object.keys(e).forEach(function (k) {
14
- if (k !== 'default') {
15
- var d = Object.getOwnPropertyDescriptor(e, k);
16
- Object.defineProperty(n, k, d.get ? d : {
17
- enumerable: true,
18
- get: function () { return e[k]; }
19
- });
20
- }
21
- });
22
- }
23
- n.default = e;
24
- return Object.freeze(n);
25
- }
26
-
27
- var yaml__namespace = /*#__PURE__*/_interopNamespaceCompat(yaml);
28
- var pdfjsLib__namespace = /*#__PURE__*/_interopNamespaceCompat(pdfjsLib);
29
-
30
- function parseTextFile(buffer, fileName, fileType) {
31
- const content = buffer.toString("utf-8");
32
- return {
33
- content,
34
- metadata: {
35
- fileName,
36
- fileType,
37
- parseTimestamp: (/* @__PURE__ */ new Date()).toISOString()
38
- }
39
- };
40
- }
41
- function parseJSONFile(buffer, fileName, fileType) {
42
- try {
43
- const content = buffer.toString("utf-8");
44
- JSON.parse(content);
45
- return {
46
- content,
47
- metadata: {
48
- fileName,
49
- fileType,
50
- parseTimestamp: (/* @__PURE__ */ new Date()).toISOString()
51
- }
52
- };
53
- } catch (error) {
54
- throw new errors.InputError(`Invalid JSON file: ${error}`);
55
- }
56
- }
57
- function parseYAMLFile(buffer, fileName, fileType) {
58
- try {
59
- const content = buffer.toString("utf-8");
60
- yaml__namespace.load(content);
61
- return {
62
- content,
63
- metadata: {
64
- fileName,
65
- fileType,
66
- parseTimestamp: (/* @__PURE__ */ new Date()).toISOString()
67
- }
68
- };
69
- } catch (error) {
70
- throw new errors.InputError(`Invalid YAML file: ${error}`);
71
- }
72
- }
73
- async function parsePDFFile(buffer, fileName, fileType) {
74
- try {
75
- const loadingTask = pdfjsLib__namespace.getDocument({
76
- data: new Uint8Array(buffer),
77
- useSystemFonts: true
78
- });
79
- const pdf = await loadingTask.promise;
80
- const textParts = [];
81
- for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
82
- const page = await pdf.getPage(pageNum);
83
- const textContent = await page.getTextContent();
84
- const pageText = textContent.items.map((item) => {
85
- if ("str" in item) {
86
- return item.str;
87
- }
88
- return "";
89
- }).filter((str) => str.trim().length > 0).join(" ");
90
- if (pageText.trim().length > 0) {
91
- textParts.push(`--- Page ${pageNum} ---
92
- ${pageText}`);
93
- }
94
- }
95
- const content = textParts.join("\n\n");
96
- return {
97
- content,
98
- metadata: {
99
- fileName,
100
- fileType,
101
- pageCount: pdf.numPages,
102
- parseTimestamp: (/* @__PURE__ */ new Date()).toISOString()
103
- }
104
- };
105
- } catch (error) {
106
- throw new errors.InputError(`Error parsing PDF: ${error}`);
107
- }
108
- }
109
- async function parseFile(buffer, fileName, fileType) {
110
- const normalizedType = fileType.toLowerCase().replace(/^\./, "");
111
- switch (normalizedType) {
112
- case constant.SupportedFileType.MARKDOWN:
113
- case constant.SupportedFileType.TEXT:
114
- case constant.SupportedFileType.LOG:
115
- return parseTextFile(buffer, fileName, fileType);
116
- case constant.SupportedFileType.JSON:
117
- return parseJSONFile(buffer, fileName, fileType);
118
- case constant.SupportedFileType.YAML:
119
- case constant.SupportedFileType.YML:
120
- return parseYAMLFile(buffer, fileName, fileType);
121
- case constant.SupportedFileType.PDF:
122
- return parsePDFFile(buffer, fileName, fileType);
123
- default:
124
- throw new errors.InputError(`Unsupported file type: ${fileType}`);
125
- }
126
- }
127
- async function toFile(buffer, filename, options) {
128
- const stream$1 = stream.Readable.from(buffer);
129
- return {
130
- name: filename,
131
- stream: stream$1,
132
- buffer,
133
- type: options?.type || "application/octet-stream"
134
- };
135
- }
136
-
137
- exports.parseFile = parseFile;
138
- exports.toFile = toFile;
139
- //# sourceMappingURL=fileParser.cjs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fileParser.cjs.js","sources":["../../../../src/service/notebooks/documents/fileParser.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 { InputError } from '@backstage/errors';\n\nimport * as yaml from 'js-yaml';\nimport * as pdfjsLib from 'pdfjs-dist';\n\nimport { Readable } from 'stream';\n\nimport { SupportedFileType } from '../../constant';\n\nexport interface ParsedDocument {\n content: string;\n metadata: {\n fileName: string;\n fileType: string;\n pageCount?: number;\n parseTimestamp: string;\n };\n}\n\n/**\n * Parse text-based files (md, txt, log)\n */\nfunction parseTextFile(\n buffer: Buffer,\n fileName: string,\n fileType: string,\n): ParsedDocument {\n const content = buffer.toString('utf-8');\n\n return {\n content,\n metadata: {\n fileName,\n fileType,\n parseTimestamp: new Date().toISOString(),\n },\n };\n}\n\n/**\n * Parse JSON files\n */\nfunction parseJSONFile(\n buffer: Buffer,\n fileName: string,\n fileType: string,\n): ParsedDocument {\n try {\n const content = buffer.toString('utf-8');\n // Validate JSON but keep original formatting\n JSON.parse(content);\n\n return {\n content,\n metadata: {\n fileName,\n fileType,\n parseTimestamp: new Date().toISOString(),\n },\n };\n } catch (error) {\n throw new InputError(`Invalid JSON file: ${error}`);\n }\n}\n\n/**\n * Parse YAML files\n */\nfunction parseYAMLFile(\n buffer: Buffer,\n fileName: string,\n fileType: string,\n): ParsedDocument {\n try {\n const content = buffer.toString('utf-8');\n // Validate YAML but keep original formatting and comments\n yaml.load(content);\n\n return {\n content,\n metadata: {\n fileName,\n fileType,\n parseTimestamp: new Date().toISOString(),\n },\n };\n } catch (error) {\n throw new InputError(`Invalid YAML file: ${error}`);\n }\n}\n\n/**\n * Parse PDF files\n * Extracts text content from PDF using PDF.js\n */\nasync function parsePDFFile(\n buffer: Buffer,\n fileName: string,\n fileType: string,\n): Promise<ParsedDocument> {\n try {\n const loadingTask = pdfjsLib.getDocument({\n data: new Uint8Array(buffer),\n useSystemFonts: true,\n });\n\n const pdf = await loadingTask.promise;\n const textParts: string[] = [];\n\n // Extract text from each page\n for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {\n const page = await pdf.getPage(pageNum);\n const textContent = await page.getTextContent();\n\n const pageText = textContent.items\n .map((item: any) => {\n if ('str' in item) {\n return item.str;\n }\n return '';\n })\n .filter((str: string) => str.trim().length > 0)\n .join(' ');\n\n if (pageText.trim().length > 0) {\n textParts.push(`--- Page ${pageNum} ---\\n${pageText}`);\n }\n }\n\n const content = textParts.join('\\n\\n');\n\n return {\n content,\n metadata: {\n fileName,\n fileType,\n pageCount: pdf.numPages,\n parseTimestamp: new Date().toISOString(),\n },\n };\n } catch (error) {\n throw new InputError(`Error parsing PDF: ${error}`);\n }\n}\n\n/**\n * Parse file based on its type\n * @param buffer - File buffer\n * @param fileName - File name\n * @param fileType - File type (md, txt, pdf, json, yaml, yml, log)\n * @returns Parsed document with content and metadata\n */\nexport async function parseFile(\n buffer: Buffer,\n fileName: string,\n fileType: string,\n): Promise<ParsedDocument> {\n // Normalize file type: lowercase and remove leading dot\n const normalizedType = fileType.toLowerCase().replace(/^\\./, '');\n\n switch (normalizedType) {\n case SupportedFileType.MARKDOWN:\n case SupportedFileType.TEXT:\n case SupportedFileType.LOG:\n return parseTextFile(buffer, fileName, fileType);\n\n case SupportedFileType.JSON:\n return parseJSONFile(buffer, fileName, fileType);\n\n case SupportedFileType.YAML:\n case SupportedFileType.YML:\n return parseYAMLFile(buffer, fileName, fileType);\n\n case SupportedFileType.PDF:\n return parsePDFFile(buffer, fileName, fileType);\n\n default:\n throw new InputError(`Unsupported file type: ${fileType}`);\n }\n}\n\n/**\n * File-like object interface matching what toFile() returns\n */\nexport interface FileObject {\n name: string;\n stream: Readable;\n buffer: Buffer;\n type: string;\n}\n\n/**\n * Convert Buffer to File-like object for upload\n */\nexport async function toFile(\n buffer: Buffer,\n filename: string,\n options?: { type?: string },\n): Promise<FileObject> {\n const stream = Readable.from(buffer);\n\n return {\n name: filename,\n stream,\n buffer,\n type: options?.type || 'application/octet-stream',\n };\n}\n"],"names":["InputError","yaml","pdfjsLib","SupportedFileType","stream","Readable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAS,aAAA,CACP,MAAA,EACA,QAAA,EACA,QAAA,EACgB;AAChB,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAEvC,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,QAAA,EAAU;AAAA,MACR,QAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACzC,GACF;AACF;AAKA,SAAS,aAAA,CACP,MAAA,EACA,QAAA,EACA,QAAA,EACgB;AAChB,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAEvC,IAAA,IAAA,CAAK,MAAM,OAAO,CAAA;AAElB,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,QAAA,EAAU;AAAA,QACR,QAAA;AAAA,QACA,QAAA;AAAA,QACA,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACzC,KACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,mBAAA,EAAsB,KAAK,CAAA,CAAE,CAAA;AAAA,EACpD;AACF;AAKA,SAAS,aAAA,CACP,MAAA,EACA,QAAA,EACA,QAAA,EACgB;AAChB,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AAEvC,IAAAC,eAAA,CAAK,KAAK,OAAO,CAAA;AAEjB,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,QAAA,EAAU;AAAA,QACR,QAAA;AAAA,QACA,QAAA;AAAA,QACA,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACzC,KACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAID,iBAAA,CAAW,CAAA,mBAAA,EAAsB,KAAK,CAAA,CAAE,CAAA;AAAA,EACpD;AACF;AAMA,eAAe,YAAA,CACb,MAAA,EACA,QAAA,EACA,QAAA,EACyB;AACzB,EAAA,IAAI;AACF,IAAA,MAAM,WAAA,GAAcE,oBAAS,WAAA,CAAY;AAAA,MACvC,IAAA,EAAM,IAAI,UAAA,CAAW,MAAM,CAAA;AAAA,MAC3B,cAAA,EAAgB;AAAA,KACjB,CAAA;AAED,IAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAY,OAAA;AAC9B,IAAA,MAAM,YAAsB,EAAC;AAG7B,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,GAAA,CAAI,UAAU,OAAA,EAAA,EAAW;AACxD,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,OAAA,CAAQ,OAAO,CAAA;AACtC,MAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,cAAA,EAAe;AAE9C,MAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,CAC1B,GAAA,CAAI,CAAC,IAAA,KAAc;AAClB,QAAA,IAAI,SAAS,IAAA,EAAM;AACjB,UAAA,OAAO,IAAA,CAAK,GAAA;AAAA,QACd;AACA,QAAA,OAAO,EAAA;AAAA,MACT,CAAC,CAAA,CACA,MAAA,CAAO,CAAC,GAAA,KAAgB,GAAA,CAAI,IAAA,EAAK,CAAE,MAAA,GAAS,CAAC,CAAA,CAC7C,IAAA,CAAK,GAAG,CAAA;AAEX,MAAA,IAAI,QAAA,CAAS,IAAA,EAAK,CAAE,MAAA,GAAS,CAAA,EAAG;AAC9B,QAAA,SAAA,CAAU,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,EAAS,QAAQ,CAAA,CAAE,CAAA;AAAA,MACvD;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA;AAErC,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,QAAA,EAAU;AAAA,QACR,QAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAW,GAAA,CAAI,QAAA;AAAA,QACf,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY;AACzC,KACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAIF,iBAAA,CAAW,CAAA,mBAAA,EAAsB,KAAK,CAAA,CAAE,CAAA;AAAA,EACpD;AACF;AASA,eAAsB,SAAA,CACpB,MAAA,EACA,QAAA,EACA,QAAA,EACyB;AAEzB,EAAA,MAAM,iBAAiB,QAAA,CAAS,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAE/D,EAAA,QAAQ,cAAA;AAAgB,IACtB,KAAKG,0BAAA,CAAkB,QAAA;AAAA,IACvB,KAAKA,0BAAA,CAAkB,IAAA;AAAA,IACvB,KAAKA,0BAAA,CAAkB,GAAA;AACrB,MAAA,OAAO,aAAA,CAAc,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,IAEjD,KAAKA,0BAAA,CAAkB,IAAA;AACrB,MAAA,OAAO,aAAA,CAAc,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,IAEjD,KAAKA,0BAAA,CAAkB,IAAA;AAAA,IACvB,KAAKA,0BAAA,CAAkB,GAAA;AACrB,MAAA,OAAO,aAAA,CAAc,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,IAEjD,KAAKA,0BAAA,CAAkB,GAAA;AACrB,MAAA,OAAO,YAAA,CAAa,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,IAEhD;AACE,MAAA,MAAM,IAAIH,iBAAA,CAAW,CAAA,uBAAA,EAA0B,QAAQ,CAAA,CAAE,CAAA;AAAA;AAE/D;AAeA,eAAsB,MAAA,CACpB,MAAA,EACA,QAAA,EACA,OAAA,EACqB;AACrB,EAAA,MAAMI,QAAA,GAASC,eAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AAEnC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,YACND,QAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA,EAAM,SAAS,IAAA,IAAQ;AAAA,GACzB;AACF;;;;;"}