@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend 3.1.0 → 3.2.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 +17 -0
- package/README.md +5 -2
- package/dist/service/attachment-validation.cjs.js +30 -0
- package/dist/service/attachment-validation.cjs.js.map +1 -0
- package/dist/service/constant.cjs.js +7 -1
- package/dist/service/constant.cjs.js.map +1 -1
- package/dist/service/router.cjs.js +141 -1
- package/dist/service/router.cjs.js.map +1 -1
- package/dist/service/validation.cjs.js +59 -6
- package/dist/service/validation.cjs.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 81b7ed2: add saved prompts endpoint
|
|
8
|
+
- 9d2012f: add model-validation route to verify whether a model is multimodal. Can now attach images to models that support it.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 21e3f43: Require `intelligent-assistant.chat.update` on `PUT /v2/conversations/:conversation_id` instead of the `intelligent-assistant.chat.create` permission.
|
|
13
|
+
- e0d0986: Updated dependency `prettier` to `3.9.6`.
|
|
14
|
+
- 8966faf: Updated dependency `prettier` to `3.9.5`.
|
|
15
|
+
- Updated dependencies [81b7ed2]
|
|
16
|
+
- Updated dependencies [e0d0986]
|
|
17
|
+
- Updated dependencies [8966faf]
|
|
18
|
+
- @red-hat-developer-hub/backstage-plugin-intelligent-assistant-common@3.2.0
|
|
19
|
+
|
|
3
20
|
## 3.1.0
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -117,11 +117,11 @@ dynamicPlugins:
|
|
|
117
117
|
red-hat-developer-hub.backstage-plugin-intelligent-assistant:
|
|
118
118
|
dynamicRoutes:
|
|
119
119
|
- path: /intelligent-assistant
|
|
120
|
-
importName: LightspeedPage
|
|
120
|
+
importName: LightspeedPage # unchanged
|
|
121
121
|
module: Legacy
|
|
122
122
|
mountPoints:
|
|
123
123
|
- mountPoint: application/internal/drawer-content
|
|
124
|
-
importName: LightspeedChatContainer
|
|
124
|
+
importName: LightspeedChatContainer # unchanged
|
|
125
125
|
module: Legacy
|
|
126
126
|
config:
|
|
127
127
|
id: intelligent-assistant
|
|
@@ -344,6 +344,9 @@ p, role:default/team_a, intelligent-assistant.notebooks.use, update, allow
|
|
|
344
344
|
p, role:default/team_a, intelligent-assistant.mcp.read, read, allow
|
|
345
345
|
p, role:default/team_a, intelligent-assistant.mcp.manage, update, allow
|
|
346
346
|
|
|
347
|
+
# Required for saved prompts
|
|
348
|
+
p, role:default/team_a, intelligent-assistant.saved-prompts.manage, update, allow
|
|
349
|
+
|
|
347
350
|
g, user:default/<your-user-name>, role:default/team_a
|
|
348
351
|
|
|
349
352
|
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4
|
+
const ModelCapabilitiesCache = {
|
|
5
|
+
cache: {},
|
|
6
|
+
get(model) {
|
|
7
|
+
const entry = this.cache[model];
|
|
8
|
+
if (!entry) return void 0;
|
|
9
|
+
if (Date.now() > entry.expiry) {
|
|
10
|
+
delete this.cache[model];
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
return entry.value;
|
|
14
|
+
},
|
|
15
|
+
set(model, supportsVision) {
|
|
16
|
+
this.cache[model] = {
|
|
17
|
+
value: supportsVision,
|
|
18
|
+
expiry: Date.now() + CACHE_TTL_MS
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
has(model) {
|
|
22
|
+
return this.get(model) !== void 0;
|
|
23
|
+
},
|
|
24
|
+
clear() {
|
|
25
|
+
this.cache = {};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
exports.ModelCapabilitiesCache = ModelCapabilitiesCache;
|
|
30
|
+
//# sourceMappingURL=attachment-validation.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment-validation.cjs.js","sources":["../../src/service/attachment-validation.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\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\nexport const ModelCapabilitiesCache = {\n cache: {} as Record<string, { value: boolean; expiry: number }>,\n\n get(model: string): boolean | undefined {\n const entry = this.cache[model];\n if (!entry) return undefined;\n if (Date.now() > entry.expiry) {\n delete this.cache[model];\n return undefined;\n }\n return entry.value;\n },\n\n set(model: string, supportsVision: boolean): void {\n this.cache[model] = {\n value: supportsVision,\n expiry: Date.now() + CACHE_TTL_MS,\n };\n },\n\n has(model: string): boolean {\n return this.get(model) !== undefined;\n },\n\n clear(): void {\n this.cache = {};\n },\n};\n"],"names":[],"mappings":";;AAgBA,MAAM,YAAA,GAAe,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAE7B,MAAM,sBAAA,GAAyB;AAAA,EACpC,OAAO,EAAC;AAAA,EAER,IAAI,KAAA,EAAoC;AACtC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC9B,IAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AACnB,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA,CAAM,MAAA,EAAQ;AAC7B,MAAA,OAAO,IAAA,CAAK,MAAM,KAAK,CAAA;AACvB,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,KAAA,CAAM,KAAA;AAAA,EACf,CAAA;AAAA,EAEA,GAAA,CAAI,OAAe,cAAA,EAA+B;AAChD,IAAA,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA,GAAI;AAAA,MAClB,KAAA,EAAO,cAAA;AAAA,MACP,MAAA,EAAQ,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,KACvB;AAAA,EACF,CAAA;AAAA,EAEA,IAAI,KAAA,EAAwB;AAC1B,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,KAAM,MAAA;AAAA,EAC7B,CAAA;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,QAAQ,EAAC;AAAA,EAChB;AACF;;;;"}
|
|
@@ -80,8 +80,13 @@ var SupportedFileType = /* @__PURE__ */ ((SupportedFileType2) => {
|
|
|
80
80
|
SupportedFileType2["LOG"] = "log";
|
|
81
81
|
return SupportedFileType2;
|
|
82
82
|
})(SupportedFileType || {});
|
|
83
|
-
const SKIP_USER_ID_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
83
|
+
const SKIP_USER_ID_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
84
|
+
"/v1/models",
|
|
85
|
+
"/v1/shields",
|
|
86
|
+
"/v1/saved-prompts/config"
|
|
87
|
+
]);
|
|
84
88
|
const DEFAULT_HISTORY_LENGTH = 10;
|
|
89
|
+
const TEST_VISION_JPEG = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCwAA//2Q==";
|
|
85
90
|
|
|
86
91
|
exports.DEFAULT_CHUNKING_STRATEGY_TYPE = DEFAULT_CHUNKING_STRATEGY_TYPE;
|
|
87
92
|
exports.DEFAULT_CHUNK_OVERLAP_TOKENS = DEFAULT_CHUNK_OVERLAP_TOKENS;
|
|
@@ -103,5 +108,6 @@ exports.NOTEBOOKS_SYSTEM_PROMPT = NOTEBOOKS_SYSTEM_PROMPT;
|
|
|
103
108
|
exports.RATE_LIMIT_WINDOW_MS = RATE_LIMIT_WINDOW_MS;
|
|
104
109
|
exports.SKIP_USER_ID_ENDPOINTS = SKIP_USER_ID_ENDPOINTS;
|
|
105
110
|
exports.SupportedFileType = SupportedFileType;
|
|
111
|
+
exports.TEST_VISION_JPEG = TEST_VISION_JPEG;
|
|
106
112
|
exports.upload = upload;
|
|
107
113
|
//# sourceMappingURL=constant.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constant.cjs.js","sources":["../../src/service/constant.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 multer from 'multer';\n\n/**\n * Default values for AI Notebooks\n */\nexport const DEFAULT_CHUNKING_STRATEGY_TYPE = 'auto'; // auto chunking\nexport const DEFAULT_MAX_CHUNK_SIZE_TOKENS = 512; // 512 tokens\nexport const DEFAULT_CHUNK_OVERLAP_TOKENS = 50; // 50 tokens\nexport const DEFAULT_LIGHTSPEED_SERVICE_HOST = '127.0.0.1'; // Lightspeed core service host\nexport const DEFAULT_LIGHTSPEED_SERVICE_PORT = 8080; // Lightspeed service port\nexport const DEFAULT_MAX_FILE_SIZE_MB = 20 * 1024 * 1024; // 20MB\n\n/**\n * Rate limiting defaults (window is fixed at 1 minute)\n */\nexport const RATE_LIMIT_WINDOW_MS = 60000;\nexport const DEFAULT_EXPENSIVE_RATE_LIMIT_MAX = 25;\nexport const DEFAULT_GENERAL_RATE_LIMIT_MAX = 200;\n\n/**\n * Input validation limits for query endpoints\n */\nexport const MAX_QUERY_LENGTH = 32000; // 32K characters (reasonable for LLM context)\nexport const MAX_ATTACHMENT_SIZE_BYTES = 20 * 1024 * 1024; // 20MB (matches notebooks limit)\nexport const MAX_TOTAL_ATTACHMENTS_SIZE_BYTES = 50 * 1024 * 1024; // 50MB total\nexport const EXPRESS_JSON_BODY_LIMIT = '60mb';\n\nexport const NOTEBOOKS_SYSTEM_PROMPT = `\nYou are a helpful, analytical Research Analyst assistant. Your primary objective is to synthesize cross-document information to answer user queries with 100% fidelity to the provided documents.\n\n### QUERY TYPES - IMPORTANT\n* **Meta Queries ONLY:** ONLY when the user asks specifically about YOU as an assistant (e.g., \"who are you\", \"what can you do\", \"hello\"), respond naturally without requiring documents.\n* **ALL OTHER QUERIES:** For ANY other question, you MUST use the strict document-grounding rules below. This includes general knowledge questions, trivia, explanations, etc. If it's not about you as an assistant, it requires document evidence.\n\n### STRICT OPERATIONAL CONSTRAINTS\n* **Zero Outside Knowledge:** Do NOT use prior training data, general knowledge, or unsupported logical leaps to answer queries.\n* **Absolute Grounding:** If the provided documents do not contain explicit, direct evidence to answer the query, you MUST output exactly: \"I cannot answer this based on the provided documents.\"\n* **Precision Citations:** Every single factual claim, metric, or conclusion must have an inline citation [Document Title].\n* **Contradictions:** Do not resolve discrepancies. If sources conflict, explicitly document the friction (e.g., \"Source A states X, whereas Source B states Y\").\n\n### ANALYTICAL GUIDELINES\n* **Comprehensive Responses:** Provide thorough, detailed analysis. Don't be overly brief - expand on the evidence with full context and explanation.\n* **Quantitative Focus:** Prioritize extracting specific metrics, dates, and figures.\n* **Objective Tone:** Use neutral, professional language. Do not use subjective adjectives (e.g., \"impressive,\" \"concerning\") unless quoting the text directly.\n\n### REQUIRED ANALYSIS PROCESS\nBefore generating your response, you must internally perform evidence extraction (DO NOT show this in your output):\n1. Identify the core entities and requirements of the user's query.\n2. Extract exact, verbatim quotes from the provided documents that directly address the query.\n3. If no explicit quotes exist to answer the prompt, output ONLY: \"I cannot answer this based on the provided documents.\"\n\n### CRITICAL: NEVER output <evidence_extraction> tags or any internal reasoning in your visible response. These are for your internal analysis only.\n\n### REQUIRED OUTPUT STRUCTURE\nWhen you have evidence from documents, structure your response as:\n\n**Executive Summary:**\n[A comprehensive 2-4 sentence synthesis of the primary findings based strictly on the extracted evidence. Provide full context and detail.]\n\n**Detailed Analysis:**\n* **[Key Entity/Theme]:** [Thorough explanation of the fact or data point derived from text, with full context and supporting details] [Document Title].\n* **[Key Entity/Theme]:** [Thorough explanation of the fact or data point derived from text, with full context and supporting details] [Document Title].\n\n**Referenced Documents:**\n* [Document Title 1]\n* [Document Title 2]\n\nWhen you lack evidence, output ONLY: \"I cannot answer this based on the provided documents.\"\n`.trim();\n\n/**\n * HTTP and networking constants\n * @reserved Reserved for future URL file type support\n */\nexport const URL_FETCH_TIMEOUT_MS = 30000; // 30 second timeout for URL fetching\nexport const USER_AGENT = 'RHDH-Notebooks-Bot/1.0'; // User agent for HTTP requests\nexport const MAX_URL_CONTENT_SIZE = 10 * 1024 * 1024; // 10MB max for URL fetched content\n\n/**\n * HTTP status codes\n */\nexport const HTTP_STATUS_ACCEPTED = 202; // Async operation accepted\nexport const HTTP_STATUS_BAD_REQUEST = 400; // Bad request\nexport const HTTP_STATUS_FORBIDDEN = 403; // Forbidden\nexport const HTTP_STATUS_NOT_FOUND = 404; // Not found\nexport const HTTP_STATUS_CONFLICT = 409; // Conflict\nexport const HTTP_STATUS_INTERNAL_ERROR = 500; // Internal server error\n\n/**\n * SSRF Protection - Blocked hostnames for security\n * These hostnames are commonly used for Server-Side Request Forgery attacks\n * @reserved Reserved for future URL file type support\n */\nexport const SSRF_BLOCKED_HOSTNAMES = [\n 'localhost',\n 'metadata.google.internal', // GCP metadata endpoint\n 'kubernetes.default.svc', // Kubernetes internal DNS\n 'host.docker.internal', // Docker host access\n '169.254.169.254', // NOSONAR local netwok, used by AWS/Azure/GCP metadata IP\n '127.0.0.1', // IPv4 loopback\n '0.0.0.0', // IPv4 any address\n '::1', // IPv6 loopback\n '::', // IPv6 any address\n] as const;\n\n/**\n * Prompt Injection Protection - Patterns to detect and sanitize\n * These patterns are commonly used in prompt injection attacks\n * @reserved Reserved for future URL file type support\n */\nexport const PROMPT_INJECTION_PATTERNS = [\n /ignore\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /disregard\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /forget\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /you\\s+are\\s+now\\s+(a\\s+)?different/gi,\n /new\\s+(instructions?|prompts?)\\s*:/gi,\n /system\\s*:\\s*/gi,\n /assistant\\s*:\\s*/gi,\n /\\[INST\\]/gi,\n /\\[\\/INST\\]/gi,\n /<\\|im_start\\|>/gi,\n /<\\|im_end\\|>/gi,\n /<\\|endoftext\\|>/gi,\n /\\[SYSTEM\\]/gi,\n /\\[\\/SYSTEM\\]/gi,\n /\\[ASSISTANT\\]/gi,\n /\\[\\/ASSISTANT\\]/gi,\n] as const;\n\n/**\n * Content sanitization constants\n * @reserved Reserved for future URL file type support\n */\nexport const MAX_CONSECUTIVE_NEWLINES = 4; // Max consecutive newlines allowed in content\nexport const FILTERED_CONTENT_MARKER = '[CONTENT_FILTERED]'; // Marker for filtered content\n\n/**\n * File type to MIME type mapping\n */\nexport const FILE_TYPE_TO_MIME: Record<string, string> = {\n txt: 'text/plain',\n md: 'text/markdown',\n log: 'text/plain',\n json: 'application/json',\n yaml: 'application/x-yaml',\n yml: 'application/x-yaml',\n pdf: 'application/pdf',\n};\n\nexport const MAX_QUERY_RETRIES = 1; // Max number of retries for query\n\nexport const upload = multer({\n storage: multer.memoryStorage(),\n limits: {\n fileSize: DEFAULT_MAX_FILE_SIZE_MB,\n },\n});\n\n/**\n * Supported file types for document upload AI Notebooks\n */\nexport enum SupportedFileType {\n MARKDOWN = 'md',\n TEXT = 'txt',\n PDF = 'pdf',\n JSON = 'json',\n YAML = 'yaml',\n YML = 'yml',\n LOG = 'log',\n}\n\n/**\n * HTML parsing constants for stripping tags and extracting text\n * @reserved Reserved for future URL file type support\n */\nexport const HTML_BLOCK_TAGS = new Set([\n 'div',\n 'p',\n 'br',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'li',\n 'tr',\n 'section',\n 'article',\n 'header',\n 'footer',\n]);\n\nexport const HTML_IGNORED_TAGS = new Set(['script', 'style']);\n\nexport const POLL_INTERVAL_MS = 1000; // 1 second\n\nexport const SKIP_USER_ID_ENDPOINTS = new Set(['/v1/models', '/v1/shields']);\n\n// default number of message history being loaded\nexport const DEFAULT_HISTORY_LENGTH = 10;\n"],"names":["multer","SupportedFileType"],"mappings":";;;;;;;;AAoBO,MAAM,8BAAA,GAAiC;AACvC,MAAM,6BAAA,GAAgC;AACtC,MAAM,4BAAA,GAA+B;AACrC,MAAM,+BAAA,GAAkC;AACxC,MAAM,+BAAA,GAAkC;AACxC,MAAM,wBAAA,GAA2B,KAAK,IAAA,GAAO;AAK7C,MAAM,oBAAA,GAAuB;AAC7B,MAAM,gCAAA,GAAmC;AACzC,MAAM,8BAAA,GAAiC;AAKvC,MAAM,gBAAA,GAAmB;AACzB,MAAM,yBAAA,GAA4B,KAAK,IAAA,GAAO;AAC9C,MAAM,gCAAA,GAAmC,KAAK,IAAA,GAAO;AACrD,MAAM,uBAAA,GAA0B;AAEhC,MAAM,uBAAA,GAA0B;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA,CAyCrC,IAAA;AAaK,MAAM,oBAAA,GAAuB;AAK7B,MAAM,0BAAA,GAA6B;AA+DnC,MAAM,iBAAA,GAAoB;AAE1B,MAAM,SAASA,uBAAA,CAAO;AAAA,EAC3B,OAAA,EAASA,wBAAO,aAAA,EAAc;AAAA,EAC9B,MAAA,EAAQ;AAAA,IACN,QAAA,EAAU;AAAA;AAEd,CAAC;AAKM,IAAK,iBAAA,qBAAAC,kBAAAA,KAAL;AACL,EAAAA,mBAAA,UAAA,CAAA,GAAW,IAAA;AACX,EAAAA,mBAAA,MAAA,CAAA,GAAO,KAAA;AACP,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,mBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,mBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AAPI,EAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;AAoCL,MAAM,yCAAyB,IAAI,GAAA,CAAI,CAAC,YAAA,EAAc,aAAa,CAAC;AAGpE,MAAM,sBAAA,GAAyB;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"constant.cjs.js","sources":["../../src/service/constant.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 multer from 'multer';\n\n/**\n * Default values for AI Notebooks\n */\nexport const DEFAULT_CHUNKING_STRATEGY_TYPE = 'auto'; // auto chunking\nexport const DEFAULT_MAX_CHUNK_SIZE_TOKENS = 512; // 512 tokens\nexport const DEFAULT_CHUNK_OVERLAP_TOKENS = 50; // 50 tokens\nexport const DEFAULT_LIGHTSPEED_SERVICE_HOST = '127.0.0.1'; // Lightspeed core service host\nexport const DEFAULT_LIGHTSPEED_SERVICE_PORT = 8080; // Lightspeed service port\nexport const DEFAULT_MAX_FILE_SIZE_MB = 20 * 1024 * 1024; // 20MB\n\n/**\n * Rate limiting defaults (window is fixed at 1 minute)\n */\nexport const RATE_LIMIT_WINDOW_MS = 60000;\nexport const DEFAULT_EXPENSIVE_RATE_LIMIT_MAX = 25;\nexport const DEFAULT_GENERAL_RATE_LIMIT_MAX = 200;\n\n/**\n * Input validation limits for query endpoints\n */\nexport const MAX_QUERY_LENGTH = 32000; // 32K characters (reasonable for LLM context)\nexport const MAX_ATTACHMENT_SIZE_BYTES = 20 * 1024 * 1024; // 20MB (matches notebooks limit)\nexport const MAX_TOTAL_ATTACHMENTS_SIZE_BYTES = 50 * 1024 * 1024; // 50MB total\nexport const EXPRESS_JSON_BODY_LIMIT = '60mb';\n\nexport const NOTEBOOKS_SYSTEM_PROMPT = `\nYou are a helpful, analytical Research Analyst assistant. Your primary objective is to synthesize cross-document information to answer user queries with 100% fidelity to the provided documents.\n\n### QUERY TYPES - IMPORTANT\n* **Meta Queries ONLY:** ONLY when the user asks specifically about YOU as an assistant (e.g., \"who are you\", \"what can you do\", \"hello\"), respond naturally without requiring documents.\n* **ALL OTHER QUERIES:** For ANY other question, you MUST use the strict document-grounding rules below. This includes general knowledge questions, trivia, explanations, etc. If it's not about you as an assistant, it requires document evidence.\n\n### STRICT OPERATIONAL CONSTRAINTS\n* **Zero Outside Knowledge:** Do NOT use prior training data, general knowledge, or unsupported logical leaps to answer queries.\n* **Absolute Grounding:** If the provided documents do not contain explicit, direct evidence to answer the query, you MUST output exactly: \"I cannot answer this based on the provided documents.\"\n* **Precision Citations:** Every single factual claim, metric, or conclusion must have an inline citation [Document Title].\n* **Contradictions:** Do not resolve discrepancies. If sources conflict, explicitly document the friction (e.g., \"Source A states X, whereas Source B states Y\").\n\n### ANALYTICAL GUIDELINES\n* **Comprehensive Responses:** Provide thorough, detailed analysis. Don't be overly brief - expand on the evidence with full context and explanation.\n* **Quantitative Focus:** Prioritize extracting specific metrics, dates, and figures.\n* **Objective Tone:** Use neutral, professional language. Do not use subjective adjectives (e.g., \"impressive,\" \"concerning\") unless quoting the text directly.\n\n### REQUIRED ANALYSIS PROCESS\nBefore generating your response, you must internally perform evidence extraction (DO NOT show this in your output):\n1. Identify the core entities and requirements of the user's query.\n2. Extract exact, verbatim quotes from the provided documents that directly address the query.\n3. If no explicit quotes exist to answer the prompt, output ONLY: \"I cannot answer this based on the provided documents.\"\n\n### CRITICAL: NEVER output <evidence_extraction> tags or any internal reasoning in your visible response. These are for your internal analysis only.\n\n### REQUIRED OUTPUT STRUCTURE\nWhen you have evidence from documents, structure your response as:\n\n**Executive Summary:**\n[A comprehensive 2-4 sentence synthesis of the primary findings based strictly on the extracted evidence. Provide full context and detail.]\n\n**Detailed Analysis:**\n* **[Key Entity/Theme]:** [Thorough explanation of the fact or data point derived from text, with full context and supporting details] [Document Title].\n* **[Key Entity/Theme]:** [Thorough explanation of the fact or data point derived from text, with full context and supporting details] [Document Title].\n\n**Referenced Documents:**\n* [Document Title 1]\n* [Document Title 2]\n\nWhen you lack evidence, output ONLY: \"I cannot answer this based on the provided documents.\"\n`.trim();\n\n/**\n * HTTP and networking constants\n * @reserved Reserved for future URL file type support\n */\nexport const URL_FETCH_TIMEOUT_MS = 30000; // 30 second timeout for URL fetching\nexport const USER_AGENT = 'RHDH-Notebooks-Bot/1.0'; // User agent for HTTP requests\nexport const MAX_URL_CONTENT_SIZE = 10 * 1024 * 1024; // 10MB max for URL fetched content\n\n/**\n * HTTP status codes\n */\nexport const HTTP_STATUS_ACCEPTED = 202; // Async operation accepted\nexport const HTTP_STATUS_BAD_REQUEST = 400; // Bad request\nexport const HTTP_STATUS_FORBIDDEN = 403; // Forbidden\nexport const HTTP_STATUS_NOT_FOUND = 404; // Not found\nexport const HTTP_STATUS_CONFLICT = 409; // Conflict\nexport const HTTP_STATUS_INTERNAL_ERROR = 500; // Internal server error\n\n/**\n * SSRF Protection - Blocked hostnames for security\n * These hostnames are commonly used for Server-Side Request Forgery attacks\n * @reserved Reserved for future URL file type support\n */\nexport const SSRF_BLOCKED_HOSTNAMES = [\n 'localhost',\n 'metadata.google.internal', // GCP metadata endpoint\n 'kubernetes.default.svc', // Kubernetes internal DNS\n 'host.docker.internal', // Docker host access\n '169.254.169.254', // NOSONAR local netwok, used by AWS/Azure/GCP metadata IP\n '127.0.0.1', // IPv4 loopback\n '0.0.0.0', // IPv4 any address\n '::1', // IPv6 loopback\n '::', // IPv6 any address\n] as const;\n\n/**\n * Prompt Injection Protection - Patterns to detect and sanitize\n * These patterns are commonly used in prompt injection attacks\n * @reserved Reserved for future URL file type support\n */\nexport const PROMPT_INJECTION_PATTERNS = [\n /ignore\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /disregard\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /forget\\s+(all\\s+)?previous\\s+(instructions?|prompts?)/gi,\n /you\\s+are\\s+now\\s+(a\\s+)?different/gi,\n /new\\s+(instructions?|prompts?)\\s*:/gi,\n /system\\s*:\\s*/gi,\n /assistant\\s*:\\s*/gi,\n /\\[INST\\]/gi,\n /\\[\\/INST\\]/gi,\n /<\\|im_start\\|>/gi,\n /<\\|im_end\\|>/gi,\n /<\\|endoftext\\|>/gi,\n /\\[SYSTEM\\]/gi,\n /\\[\\/SYSTEM\\]/gi,\n /\\[ASSISTANT\\]/gi,\n /\\[\\/ASSISTANT\\]/gi,\n] as const;\n\n/**\n * Content sanitization constants\n * @reserved Reserved for future URL file type support\n */\nexport const MAX_CONSECUTIVE_NEWLINES = 4; // Max consecutive newlines allowed in content\nexport const FILTERED_CONTENT_MARKER = '[CONTENT_FILTERED]'; // Marker for filtered content\n\n/**\n * File type to MIME type mapping\n */\nexport const FILE_TYPE_TO_MIME: Record<string, string> = {\n txt: 'text/plain',\n md: 'text/markdown',\n log: 'text/plain',\n json: 'application/json',\n yaml: 'application/x-yaml',\n yml: 'application/x-yaml',\n pdf: 'application/pdf',\n};\n\nexport const MAX_QUERY_RETRIES = 1; // Max number of retries for query\n\nexport const upload = multer({\n storage: multer.memoryStorage(),\n limits: {\n fileSize: DEFAULT_MAX_FILE_SIZE_MB,\n },\n});\n\n/**\n * Supported file types for document upload AI Notebooks\n */\nexport enum SupportedFileType {\n MARKDOWN = 'md',\n TEXT = 'txt',\n PDF = 'pdf',\n JSON = 'json',\n YAML = 'yaml',\n YML = 'yml',\n LOG = 'log',\n}\n\n/**\n * HTML parsing constants for stripping tags and extracting text\n * @reserved Reserved for future URL file type support\n */\nexport const HTML_BLOCK_TAGS = new Set([\n 'div',\n 'p',\n 'br',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'li',\n 'tr',\n 'section',\n 'article',\n 'header',\n 'footer',\n]);\n\nexport const HTML_IGNORED_TAGS = new Set(['script', 'style']);\n\nexport const POLL_INTERVAL_MS = 1000; // 1 second\n\nexport const SKIP_USER_ID_ENDPOINTS = new Set([\n '/v1/models',\n '/v1/shields',\n '/v1/saved-prompts/config',\n]);\n\n// default number of message history being loaded\nexport const DEFAULT_HISTORY_LENGTH = 10;\n\n/**\n * Minimal 1x1 pixel JPEG image for testing model vision capabilities.\n * Base64-encoded JPEG with minimal headers.\n */\nexport const TEST_VISION_JPEG =\n '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a' +\n 'HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy' +\n 'MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIA' +\n 'AhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEB' +\n 'AQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCwAA//2Q==';\n"],"names":["multer","SupportedFileType"],"mappings":";;;;;;;;AAoBO,MAAM,8BAAA,GAAiC;AACvC,MAAM,6BAAA,GAAgC;AACtC,MAAM,4BAAA,GAA+B;AACrC,MAAM,+BAAA,GAAkC;AACxC,MAAM,+BAAA,GAAkC;AACxC,MAAM,wBAAA,GAA2B,KAAK,IAAA,GAAO;AAK7C,MAAM,oBAAA,GAAuB;AAC7B,MAAM,gCAAA,GAAmC;AACzC,MAAM,8BAAA,GAAiC;AAKvC,MAAM,gBAAA,GAAmB;AACzB,MAAM,yBAAA,GAA4B,KAAK,IAAA,GAAO;AAC9C,MAAM,gCAAA,GAAmC,KAAK,IAAA,GAAO;AACrD,MAAM,uBAAA,GAA0B;AAEhC,MAAM,uBAAA,GAA0B;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA,CAyCrC,IAAA;AAaK,MAAM,oBAAA,GAAuB;AAK7B,MAAM,0BAAA,GAA6B;AA+DnC,MAAM,iBAAA,GAAoB;AAE1B,MAAM,SAASA,uBAAA,CAAO;AAAA,EAC3B,OAAA,EAASA,wBAAO,aAAA,EAAc;AAAA,EAC9B,MAAA,EAAQ;AAAA,IACN,QAAA,EAAU;AAAA;AAEd,CAAC;AAKM,IAAK,iBAAA,qBAAAC,kBAAAA,KAAL;AACL,EAAAA,mBAAA,UAAA,CAAA,GAAW,IAAA;AACX,EAAAA,mBAAA,MAAA,CAAA,GAAO,KAAA;AACP,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,mBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,mBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AACN,EAAAA,mBAAA,KAAA,CAAA,GAAM,KAAA;AAPI,EAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;AAoCL,MAAM,sBAAA,uBAA6B,GAAA,CAAI;AAAA,EAC5C,YAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,sBAAA,GAAyB;AAM/B,MAAM,gBAAA,GACX;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -7,6 +7,7 @@ var express = require('express');
|
|
|
7
7
|
var httpProxyMiddleware = require('http-proxy-middleware');
|
|
8
8
|
var backstagePluginIntelligentAssistantCommon = require('@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common');
|
|
9
9
|
var node_stream = require('node:stream');
|
|
10
|
+
var attachmentValidation = require('./attachment-validation.cjs.js');
|
|
10
11
|
var constant = require('./constant.cjs.js');
|
|
11
12
|
var mcpServerStore = require('./mcp-server-store.cjs.js');
|
|
12
13
|
var mcpServerValidator = require('./mcp-server-validator.cjs.js');
|
|
@@ -442,6 +443,25 @@ async function createRouter(options) {
|
|
|
442
443
|
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
|
|
443
444
|
apiProxy
|
|
444
445
|
);
|
|
446
|
+
router.get(
|
|
447
|
+
"/v1/saved-prompts/config",
|
|
448
|
+
generalRateLimiter,
|
|
449
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedSavedPromptsManagePermission),
|
|
450
|
+
apiProxy
|
|
451
|
+
// SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint
|
|
452
|
+
);
|
|
453
|
+
router.get(
|
|
454
|
+
"/v1/saved-prompts",
|
|
455
|
+
generalRateLimiter,
|
|
456
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedSavedPromptsManagePermission),
|
|
457
|
+
apiProxy
|
|
458
|
+
);
|
|
459
|
+
router.delete(
|
|
460
|
+
"/v1/saved-prompts/:prompt_id",
|
|
461
|
+
generalRateLimiter,
|
|
462
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedSavedPromptsManagePermission),
|
|
463
|
+
apiProxy
|
|
464
|
+
);
|
|
445
465
|
router.post(
|
|
446
466
|
"/v1/feedback",
|
|
447
467
|
generalRateLimiter,
|
|
@@ -480,6 +500,46 @@ async function createRouter(options) {
|
|
|
480
500
|
}
|
|
481
501
|
}
|
|
482
502
|
);
|
|
503
|
+
router.post(
|
|
504
|
+
"/v1/saved-prompts",
|
|
505
|
+
generalRateLimiter,
|
|
506
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedSavedPromptsManagePermission),
|
|
507
|
+
async (request, response) => {
|
|
508
|
+
try {
|
|
509
|
+
const { userEntityRef } = getIdentity.getIdentity(request);
|
|
510
|
+
logger.info(
|
|
511
|
+
`/v1/saved-prompts receives call from user: ${userEntityRef}`
|
|
512
|
+
);
|
|
513
|
+
const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;
|
|
514
|
+
const requestBody = JSON.stringify(request.body);
|
|
515
|
+
const fetchResponse = await fetch(
|
|
516
|
+
`${lcsBaseUrl}/v1/saved-prompts?${userQueryParam}`,
|
|
517
|
+
{
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: {
|
|
520
|
+
"Content-Type": "application/json"
|
|
521
|
+
},
|
|
522
|
+
body: requestBody
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
if (!fetchResponse.ok) {
|
|
526
|
+
await utils.handleLCSFetchError(
|
|
527
|
+
fetchResponse,
|
|
528
|
+
logger,
|
|
529
|
+
"creating saved prompt",
|
|
530
|
+
response
|
|
531
|
+
);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const data = await fetchResponse.json();
|
|
535
|
+
response.status(fetchResponse.status).json(data);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
const errormsg = `Error while creating saved prompt: ${error}`;
|
|
538
|
+
logger.error(errormsg);
|
|
539
|
+
response.status(500).json({ error: errormsg });
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
);
|
|
483
543
|
router.post(
|
|
484
544
|
"/v1/query/interrupt",
|
|
485
545
|
generalRateLimiter,
|
|
@@ -521,12 +581,18 @@ async function createRouter(options) {
|
|
|
521
581
|
express__default.default.json({ limit: constant.EXPRESS_JSON_BODY_LIMIT }),
|
|
522
582
|
expensiveRateLimiter,
|
|
523
583
|
validation.validateCompletionsRequest,
|
|
584
|
+
validation.validateAttachmentsForModel,
|
|
524
585
|
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatCreatePermission),
|
|
525
586
|
async (request, response) => {
|
|
526
587
|
const { provider } = request.body;
|
|
527
588
|
try {
|
|
528
589
|
const { userEntityRef, credentials } = getIdentity.getIdentity(request);
|
|
529
590
|
logger.info(`/v1/query receives call from user: ${userEntityRef}`);
|
|
591
|
+
if (request.body.attachments?.length) {
|
|
592
|
+
logger.info(
|
|
593
|
+
`/v1/query includes ${request.body.attachments.length} attachment(s): ${request.body.attachments.map((a) => a.attachment_type).join(", ")}`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
530
596
|
const userQueryParam = `user_id=${encodeURIComponent(userEntityRef)}`;
|
|
531
597
|
request.body.media_type = "application/json";
|
|
532
598
|
if (system_prompt && system_prompt.trim().length > 0) {
|
|
@@ -593,7 +659,7 @@ async function createRouter(options) {
|
|
|
593
659
|
router.put(
|
|
594
660
|
"/v2/conversations/:conversation_id",
|
|
595
661
|
generalRateLimiter,
|
|
596
|
-
requirePermission(backstagePluginIntelligentAssistantCommon.
|
|
662
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatUpdatePermission),
|
|
597
663
|
async (request, response) => {
|
|
598
664
|
try {
|
|
599
665
|
const { userEntityRef } = getIdentity.getIdentity(request);
|
|
@@ -628,6 +694,80 @@ async function createRouter(options) {
|
|
|
628
694
|
}
|
|
629
695
|
}
|
|
630
696
|
);
|
|
697
|
+
router.post(
|
|
698
|
+
"/v1/validate-model-vision",
|
|
699
|
+
generalRateLimiter,
|
|
700
|
+
requirePermission(backstagePluginIntelligentAssistantCommon.lightspeedChatReadPermission),
|
|
701
|
+
async (request, response) => {
|
|
702
|
+
const { model, provider } = request.body;
|
|
703
|
+
if (typeof model !== "string" || !model.trim() || typeof provider !== "string" || !provider.trim()) {
|
|
704
|
+
response.status(400).json({ error: "model and provider are required" });
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const cacheKey = `${provider}/${model}`;
|
|
708
|
+
try {
|
|
709
|
+
logger.info(`Vision validation requested for model: ${cacheKey}`);
|
|
710
|
+
if (attachmentValidation.ModelCapabilitiesCache.has(cacheKey)) {
|
|
711
|
+
const cached = attachmentValidation.ModelCapabilitiesCache.get(cacheKey);
|
|
712
|
+
logger.info(`Cache hit for ${cacheKey}: ${cached}`);
|
|
713
|
+
response.json({ model, provider, supportsVision: cached });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const testJpeg = `data:image/jpeg;base64,${constant.TEST_VISION_JPEG}`;
|
|
717
|
+
const testResponse = await fetch(`${lcsBaseUrl}/v1/responses`, {
|
|
718
|
+
method: "POST",
|
|
719
|
+
headers: { "Content-Type": "application/json" },
|
|
720
|
+
body: JSON.stringify({
|
|
721
|
+
model: cacheKey,
|
|
722
|
+
input: [
|
|
723
|
+
{
|
|
724
|
+
type: "message",
|
|
725
|
+
role: "user",
|
|
726
|
+
content: [
|
|
727
|
+
{
|
|
728
|
+
type: "input_image",
|
|
729
|
+
image_url: testJpeg,
|
|
730
|
+
detail: "low"
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
type: "input_text",
|
|
734
|
+
text: "hi, respond with hi."
|
|
735
|
+
}
|
|
736
|
+
]
|
|
737
|
+
}
|
|
738
|
+
],
|
|
739
|
+
tool_choice: "none",
|
|
740
|
+
temperature: 0,
|
|
741
|
+
store: false,
|
|
742
|
+
stream: false
|
|
743
|
+
})
|
|
744
|
+
});
|
|
745
|
+
if (testResponse.ok) {
|
|
746
|
+
attachmentValidation.ModelCapabilitiesCache.set(cacheKey, true);
|
|
747
|
+
response.json({ model, provider, supportsVision: true });
|
|
748
|
+
} else if (testResponse.status >= 500) {
|
|
749
|
+
logger.warn(
|
|
750
|
+
`Vision test for ${cacheKey}: ${testResponse.status} ${testResponse.statusText}, not caching`
|
|
751
|
+
);
|
|
752
|
+
response.status(502).json({
|
|
753
|
+
error: `Unable to verify vision support for model \u2014 upstream returned ${testResponse.status}`,
|
|
754
|
+
model,
|
|
755
|
+
provider
|
|
756
|
+
});
|
|
757
|
+
} else {
|
|
758
|
+
attachmentValidation.ModelCapabilitiesCache.set(cacheKey, false);
|
|
759
|
+
response.json({ model, provider, supportsVision: false });
|
|
760
|
+
}
|
|
761
|
+
} catch (error) {
|
|
762
|
+
logger.error(`Vision test error for ${cacheKey}:`, error);
|
|
763
|
+
response.status(502).json({
|
|
764
|
+
error: "Unable to verify vision support for model \u2014 upstream unreachable",
|
|
765
|
+
model,
|
|
766
|
+
provider
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
);
|
|
631
771
|
router.use((_request, response) => {
|
|
632
772
|
response.status(404).json({ error: "Requested path is not available" });
|
|
633
773
|
});
|
|
@@ -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 lightspeedChatCreatePermission,\n lightspeedChatDeletePermission,\n lightspeedChatReadPermission,\n lightspeedMcpManagePermission,\n lightspeedMcpReadPermission,\n lightspeedPermissions,\n} from '@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common';\n\nimport { Readable } from 'node:stream';\n\nimport {\n DEFAULT_LIGHTSPEED_SERVICE_HOST,\n DEFAULT_LIGHTSPEED_SERVICE_PORT,\n EXPRESS_JSON_BODY_LIMIT,\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 { validateCompletionsRequest } 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: lightspeedPermissions,\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(lightspeedMcpReadPermission),\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(lightspeedMcpReadPermission),\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(lightspeedMcpManagePermission),\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(lightspeedMcpManagePermission),\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 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\n | 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(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v1/shields',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.delete(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(lightspeedChatDeletePermission),\n apiProxy,\n );\n router.get(\n '/v1/feedback/status',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n\n router.post(\n '/v1/feedback',\n generalRateLimiter,\n requirePermission(lightspeedChatCreatePermission),\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/query/interrupt',\n generalRateLimiter,\n requirePermission(lightspeedChatCreatePermission),\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 requirePermission(lightspeedChatCreatePermission),\n async (request, response) => {\n const { provider }: Pick<QueryRequestBody, 'provider'> = request.body;\n try {\n const { userEntityRef, credentials } = getIdentity(request);\n\n logger.info(`/v1/query receives call from user: ${userEntityRef}`);\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(lightspeedChatCreatePermission),\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.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","lightspeedPermissions","createIdentityMiddleware","userPermissionAuthorization","createPermissionMiddleware","createRateLimitMiddleware","lightspeedMcpReadPermission","getIdentity","lightspeedMcpManagePermission","NotAllowedError","lightspeedChatReadPermission","lightspeedChatDeletePermission","lightspeedChatCreatePermission","handleLCSFetchError","EXPRESS_JSON_BODY_LIMIT","validateCompletionsRequest","Readable","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 lightspeedChatCreatePermission,\n lightspeedChatDeletePermission,\n lightspeedChatReadPermission,\n lightspeedChatUpdatePermission,\n lightspeedMcpManagePermission,\n lightspeedMcpReadPermission,\n lightspeedPermissions,\n lightspeedSavedPromptsManagePermission,\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: lightspeedPermissions,\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(lightspeedMcpReadPermission),\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(lightspeedMcpReadPermission),\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(lightspeedMcpManagePermission),\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(lightspeedMcpManagePermission),\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 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(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v1/shields',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.get(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n router.delete(\n '/v2/conversations/:conversation_id',\n generalRateLimiter,\n requirePermission(lightspeedChatDeletePermission),\n apiProxy,\n );\n router.get(\n '/v1/feedback/status',\n generalRateLimiter,\n requirePermission(lightspeedChatReadPermission),\n apiProxy,\n );\n\n router.get(\n '/v1/saved-prompts/config',\n generalRateLimiter,\n requirePermission(lightspeedSavedPromptsManagePermission),\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(lightspeedSavedPromptsManagePermission),\n apiProxy,\n );\n router.delete(\n '/v1/saved-prompts/:prompt_id',\n generalRateLimiter,\n requirePermission(lightspeedSavedPromptsManagePermission),\n apiProxy,\n );\n\n router.post(\n '/v1/feedback',\n generalRateLimiter,\n requirePermission(lightspeedChatCreatePermission),\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(lightspeedSavedPromptsManagePermission),\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(lightspeedChatCreatePermission),\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(lightspeedChatCreatePermission),\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\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(lightspeedChatUpdatePermission),\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(lightspeedChatReadPermission),\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 } else if (testResponse.status >= 500) {\n logger.warn(\n `Vision test for ${cacheKey}: ${testResponse.status} ${testResponse.statusText}, not caching`,\n );\n response.status(502).json({\n error: `Unable to verify vision support for model — upstream returned ${testResponse.status}`,\n model,\n provider,\n });\n } else {\n ModelCapabilitiesCache.set(cacheKey, false);\n response.json({ model, provider, supportsVision: false });\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","lightspeedPermissions","createIdentityMiddleware","userPermissionAuthorization","createPermissionMiddleware","createRateLimitMiddleware","lightspeedMcpReadPermission","getIdentity","lightspeedMcpManagePermission","NotAllowedError","lightspeedChatReadPermission","lightspeedChatDeletePermission","lightspeedSavedPromptsManagePermission","lightspeedChatCreatePermission","handleLCSFetchError","EXPRESS_JSON_BODY_LIMIT","validateCompletionsRequest","validateAttachmentsForModel","Readable","lightspeedChatUpdatePermission","ModelCapabilitiesCache","TEST_VISION_JPEG","MiddlewareFactory"],"mappings":"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"}
|
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var attachmentValidation = require('./attachment-validation.cjs.js');
|
|
3
4
|
var constant = require('./constant.cjs.js');
|
|
4
5
|
|
|
6
|
+
const JPEG_MAGIC = [255, 216, 255];
|
|
7
|
+
function extractBase64(content) {
|
|
8
|
+
const commaIndex = content.indexOf(",");
|
|
9
|
+
if (commaIndex !== -1 && content.startsWith("data:")) {
|
|
10
|
+
return content.slice(commaIndex + 1);
|
|
11
|
+
}
|
|
12
|
+
return content;
|
|
13
|
+
}
|
|
14
|
+
function hasValidJpegMagicBytes(content) {
|
|
15
|
+
const bytes = Buffer.from(extractBase64(content), "base64");
|
|
16
|
+
return bytes.length >= JPEG_MAGIC.length && JPEG_MAGIC.every((b, i) => bytes[i] === b);
|
|
17
|
+
}
|
|
18
|
+
function isValidJson(content) {
|
|
19
|
+
try {
|
|
20
|
+
JSON.parse(content);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
5
26
|
function validateAttachments(attachments) {
|
|
6
27
|
let totalSize = 0;
|
|
7
28
|
for (const attachment of attachments) {
|
|
8
29
|
const attachmentSize = attachment.content ? Buffer.byteLength(attachment.content, "utf8") : 0;
|
|
9
30
|
if (attachmentSize > constant.MAX_ATTACHMENT_SIZE_BYTES) {
|
|
10
|
-
return `Attachment "${attachment.
|
|
31
|
+
return `Attachment with type "${attachment.attachment_type}" exceeds maximum size of ${constant.MAX_ATTACHMENT_SIZE_BYTES / (1024 * 1024)}MB`;
|
|
11
32
|
}
|
|
12
33
|
totalSize += attachmentSize;
|
|
34
|
+
if (attachment.attachment_type === "image" && attachment.content && !hasValidJpegMagicBytes(attachment.content)) {
|
|
35
|
+
return "Image attachment does not contain a valid JPEG file";
|
|
36
|
+
}
|
|
37
|
+
if (attachment.content_type === "application/json" && attachment.content && !isValidJson(attachment.content)) {
|
|
38
|
+
return 'Attachment with content_type "application/json" contains invalid JSON';
|
|
39
|
+
}
|
|
13
40
|
}
|
|
14
41
|
if (totalSize > constant.MAX_TOTAL_ATTACHMENTS_SIZE_BYTES) {
|
|
15
42
|
return `Total attachments size exceeds maximum of ${constant.MAX_TOTAL_ATTACHMENTS_SIZE_BYTES / (1024 * 1024)}MB`;
|
|
@@ -32,14 +59,40 @@ const validateCompletionsRequest = (req, res, next) => {
|
|
|
32
59
|
error: `query exceeds maximum length of ${constant.MAX_QUERY_LENGTH} characters`
|
|
33
60
|
});
|
|
34
61
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
62
|
+
return next();
|
|
63
|
+
};
|
|
64
|
+
const validateAttachmentsForModel = (req, res, next) => {
|
|
65
|
+
const { model, provider, attachments } = req.body;
|
|
66
|
+
if (!attachments || attachments.length === 0) {
|
|
67
|
+
return next();
|
|
68
|
+
}
|
|
69
|
+
const attachmentError = validateAttachments(attachments);
|
|
70
|
+
if (attachmentError) {
|
|
71
|
+
return res.status(400).json({ error: attachmentError });
|
|
72
|
+
}
|
|
73
|
+
const hasImages = attachments.some(
|
|
74
|
+
(att) => att.attachment_type === "image"
|
|
75
|
+
);
|
|
76
|
+
if (!hasImages) {
|
|
77
|
+
return next();
|
|
78
|
+
}
|
|
79
|
+
const cacheKey = `${provider}/${model}`;
|
|
80
|
+
if (!attachmentValidation.ModelCapabilitiesCache.has(cacheKey)) {
|
|
81
|
+
return res.status(400).json({
|
|
82
|
+
error: "Model vision capability not validated. Please call /v1/validate-model-vision first.",
|
|
83
|
+
model
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const supportsVision = attachmentValidation.ModelCapabilitiesCache.get(cacheKey);
|
|
87
|
+
if (!supportsVision) {
|
|
88
|
+
return res.status(400).json({
|
|
89
|
+
error: "This model does not support JPEG images. Please select a vision-capable model.",
|
|
90
|
+
model
|
|
91
|
+
});
|
|
40
92
|
}
|
|
41
93
|
return next();
|
|
42
94
|
};
|
|
43
95
|
|
|
96
|
+
exports.validateAttachmentsForModel = validateAttachmentsForModel;
|
|
44
97
|
exports.validateCompletionsRequest = validateCompletionsRequest;
|
|
45
98
|
//# sourceMappingURL=validation.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.cjs.js","sources":["../../src/service/validation.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 { NextFunction, Request, Response } from 'express';\n\nimport {\n MAX_ATTACHMENT_SIZE_BYTES,\n MAX_QUERY_LENGTH,\n MAX_TOTAL_ATTACHMENTS_SIZE_BYTES,\n} from './constant';\nimport { QueryRequestBody } from './types';\n\nfunction
|
|
1
|
+
{"version":3,"file":"validation.cjs.js","sources":["../../src/service/validation.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 { NextFunction, Request, Response } from 'express';\n\nimport { ModelCapabilitiesCache } from './attachment-validation';\nimport {\n MAX_ATTACHMENT_SIZE_BYTES,\n MAX_QUERY_LENGTH,\n MAX_TOTAL_ATTACHMENTS_SIZE_BYTES,\n} from './constant';\nimport { Attachments, QueryRequestBody } from './types';\n\nconst JPEG_MAGIC = [0xff, 0xd8, 0xff];\n\nfunction extractBase64(content: string): string {\n const commaIndex = content.indexOf(',');\n if (commaIndex !== -1 && content.startsWith('data:')) {\n return content.slice(commaIndex + 1);\n }\n return content;\n}\n\nfunction hasValidJpegMagicBytes(content: string): boolean {\n const bytes = Buffer.from(extractBase64(content), 'base64');\n return (\n bytes.length >= JPEG_MAGIC.length &&\n JPEG_MAGIC.every((b, i) => bytes[i] === b)\n );\n}\n\nfunction isValidJson(content: string): boolean {\n try {\n JSON.parse(content);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction validateAttachments(attachments: Array<Attachments>): string | null {\n let totalSize = 0;\n\n for (const attachment of attachments) {\n const attachmentSize = attachment.content\n ? Buffer.byteLength(attachment.content, 'utf8')\n : 0;\n\n if (attachmentSize > MAX_ATTACHMENT_SIZE_BYTES) {\n return `Attachment with type \"${attachment.attachment_type}\" exceeds maximum size of ${MAX_ATTACHMENT_SIZE_BYTES / (1024 * 1024)}MB`;\n }\n\n totalSize += attachmentSize;\n\n if (\n attachment.attachment_type === 'image' &&\n attachment.content &&\n !hasValidJpegMagicBytes(attachment.content)\n ) {\n return 'Image attachment does not contain a valid JPEG file';\n }\n\n if (\n attachment.content_type === 'application/json' &&\n attachment.content &&\n !isValidJson(attachment.content)\n ) {\n return 'Attachment with content_type \"application/json\" contains invalid JSON';\n }\n }\n\n if (totalSize > MAX_TOTAL_ATTACHMENTS_SIZE_BYTES) {\n return `Total attachments size exceeds maximum of ${MAX_TOTAL_ATTACHMENTS_SIZE_BYTES / (1024 * 1024)}MB`;\n }\n\n return null;\n}\n\nexport const validateCompletionsRequest = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n const reqData: QueryRequestBody = req.body;\n\n if (typeof reqData.model !== 'string' || reqData.model.trim() === '') {\n return res\n .status(400)\n .json({ error: 'model is required and must be a non-empty string' });\n }\n\n if (typeof reqData.provider !== 'string' || reqData.provider.trim() === '') {\n return res\n .status(400)\n .json({ error: 'provider is required and must be a non-empty string' });\n }\n\n if (typeof reqData.query !== 'string' || reqData.query.trim() === '') {\n return res\n .status(400)\n .json({ error: 'query is required and must be a non-empty string' });\n }\n\n if (reqData.query.length > MAX_QUERY_LENGTH) {\n return res.status(400).json({\n error: `query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`,\n });\n }\n\n return next();\n};\n\nexport const validateLoadHistoryRequest = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n const historyLength = Number(req.query.historyLength);\n\n if (historyLength && !Number.isInteger(historyLength)) {\n return res.status(400).send('historyLength has to be a valid integer');\n }\n\n // TODO: Need to extract out the user_id from conversation_id, and verify with the login user entity\n\n return next();\n};\n\nexport const validateAttachmentsForModel = (\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n const { model, provider, attachments } = req.body;\n\n if (!attachments || attachments.length === 0) {\n return next();\n }\n\n const attachmentError = validateAttachments(attachments);\n if (attachmentError) {\n return res.status(400).json({ error: attachmentError });\n }\n\n const hasImages = attachments.some(\n (att: { attachment_type: string }) => att.attachment_type === 'image',\n );\n\n if (!hasImages) {\n return next();\n }\n\n const cacheKey = `${provider}/${model}`;\n\n // Check if model has been validated\n if (!ModelCapabilitiesCache.has(cacheKey)) {\n return res.status(400).json({\n error:\n 'Model vision capability not validated. Please call /v1/validate-model-vision first.',\n model,\n });\n }\n\n // Check if model supports vision\n const supportsVision = ModelCapabilitiesCache.get(cacheKey);\n if (!supportsVision) {\n return res.status(400).json({\n error:\n 'This model does not support JPEG images. Please select a vision-capable model.',\n model,\n });\n }\n\n return next();\n};\n"],"names":["MAX_ATTACHMENT_SIZE_BYTES","MAX_TOTAL_ATTACHMENTS_SIZE_BYTES","MAX_QUERY_LENGTH","ModelCapabilitiesCache"],"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": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@backstage/plugin-permission-node": "^0.11.1",
|
|
59
59
|
"@langchain/core": "^0.3.80",
|
|
60
60
|
"@langchain/openai": "^0.6.0",
|
|
61
|
-
"@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common": "^3.
|
|
61
|
+
"@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common": "^3.2.0",
|
|
62
62
|
"express": "^4.21.1",
|
|
63
63
|
"express-rate-limit": "^8.2.2",
|
|
64
64
|
"form-data": "^4.0.6",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"@types/multer": "^1.4.12",
|
|
80
80
|
"@types/supertest": "2.0.16",
|
|
81
81
|
"msw": "2.14.6",
|
|
82
|
-
"prettier": "3.
|
|
82
|
+
"prettier": "3.9.6",
|
|
83
83
|
"supertest": "6.3.4"
|
|
84
84
|
},
|
|
85
85
|
"resolutions": {
|