@red-hat-developer-hub/backstage-plugin-lightspeed 2.7.1 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## @red-hat-developer-hub/backstage-plugin-lightspeed
2
2
 
3
+ ## 2.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3c9c556: - Hide notebooks tab when `lightspeed.notebooks.enabled: false` in config
8
+ - Fix notebook queries to display correct model from config instead of chat's selected model
9
+ - Add `/notebook-conversation-ids` endpoint to filter notebook conversations from chat list even when notebooks disabled
10
+
11
+ ### Patch Changes
12
+
13
+ - a866b21: fixed dark theme issues
14
+ - 17e0527: Updated dependency `@patternfly/react-core` to `6.4.3`.
15
+ Updated dependency `@red-hat-developer-hub/backstage-plugin-app-react` to `^0.0.5`.
16
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.8.0
17
+
3
18
  ## 2.7.1
4
19
 
5
20
  ### Patch Changes
package/config.d.ts CHANGED
@@ -36,5 +36,35 @@ export interface Config {
36
36
  */
37
37
  message: string;
38
38
  }>;
39
+ /**
40
+ * Configuration for AI Notebooks
41
+ * @visibility frontend
42
+ */
43
+ notebooks?: {
44
+ /**
45
+ * Enable/disable AI Notebooks feature
46
+ * When enabled, exposes AI Notebooks REST API endpoints for document-based conversations with RAG.
47
+ * Requires Lightspeed service to be running (default: http://0.0.0.0:8080).
48
+ * @default false
49
+ * @visibility frontend
50
+ */
51
+ enabled: boolean;
52
+ /**
53
+ * Query configuration for notebooks
54
+ * @visibility frontend
55
+ */
56
+ queryDefaults?: {
57
+ /**
58
+ * Model to use for answering queries
59
+ * @visibility frontend
60
+ */
61
+ model: string;
62
+ /**
63
+ * AI provider for the query model
64
+ * @visibility frontend
65
+ */
66
+ provider_id: string;
67
+ };
68
+ };
39
69
  };
40
70
  }
@@ -115,6 +115,17 @@ class LightspeedApiClient {
115
115
  const response = await result.json();
116
116
  return response.conversations ?? [];
117
117
  }
118
+ async getNotebookConversationIds() {
119
+ const baseUrl = await this.getBaseUrl();
120
+ const result = await this.fetcher(`${baseUrl}/notebook-conversation-ids`);
121
+ if (!result.ok) {
122
+ throw new Error(
123
+ `failed to get notebook conversation IDs, status ${result.status}: ${result.statusText}`
124
+ );
125
+ }
126
+ const response = await result.json();
127
+ return response.conversation_ids ?? [];
128
+ }
118
129
  async stopMessage(requestId) {
119
130
  const baseUrl = await this.getBaseUrl();
120
131
  const response = await this.fetchApi.fetch(
@@ -1 +1 @@
1
- {"version":3,"file":"LightspeedApiClient.esm.js","sources":["../../src/api/LightspeedApiClient.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 { ConfigApi, FetchApi } from '@backstage/core-plugin-api';\n\nimport {\n TEMP_CONVERSATION_ID,\n VALID_TOPIC_RESTRICTION_PROVIDER_IDS,\n} from '../const';\nimport { Attachment, CaptureFeedback } from '../types';\nimport { LightspeedAPI } from './api';\n\n/**\n * @public\n * Lightspeed API client options\n */\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * Lightspeed API client implementation\n */\n\nexport class LightspeedApiClient implements LightspeedAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: Options) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n conversation_id:\n conversation_id === TEMP_CONVERSATION_ID\n ? undefined\n : conversation_id,\n model: selectedModel,\n provider: selectedProvider,\n query: prompt,\n attachments,\n }),\n });\n\n if (!response.body) {\n throw new Error('Readable stream is not supported or there is no body.');\n }\n\n if (!response.ok) {\n const body = await response.body.getReader();\n const reader = body.read();\n const decoder = new TextDecoder('utf-8');\n const text = await reader.then(({ done, value }) => {\n if (done) {\n return '';\n }\n return decoder.decode(value);\n });\n const errorMessage = JSON.parse(text);\n if (errorMessage?.error) {\n throw new Error(`failed to create message: ${errorMessage.error}`);\n }\n }\n return response.body.getReader();\n }\n\n private async fetcher(url: string) {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(\n `failed to fetch data, status ${response.status}: ${response.statusText}`,\n );\n }\n return response;\n }\n\n async isTopicRestrictionEnabled() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/shields`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get shields, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n if (!response?.shields || !Array.isArray(response.shields)) {\n return false;\n }\n\n return response.shields.some((shield: any) => {\n const providerId = shield.provider_resource_id;\n\n return VALID_TOPIC_RESTRICTION_PROVIDER_IDS.some(\n validId => providerId.toLowerCase() === validId.toLowerCase(),\n );\n });\n }\n\n async getAllModels() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/models`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get models, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n\n return response?.models ? response?.models : [];\n }\n\n async getConversationMessages(conversation_id: string) {\n if (conversation_id === TEMP_CONVERSATION_ID) {\n return [];\n }\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n );\n if (!result.ok) {\n throw new Error(\n `failed to get conversation messages, status ${result.status}: ${result.statusText}`,\n );\n }\n const response = await result.json();\n return response.chat_history ?? [];\n }\n\n async getConversations() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v2/conversations`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get conversation, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n return response.conversations ?? [];\n }\n\n async stopMessage(requestId: string): Promise<{ success: boolean }> {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/query/interrupt`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ request_id: requestId }),\n },\n );\n if (!response.ok) {\n throw new Error(\n `failed to stop message, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n }\n async deleteConversation(conversation_id: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n let errorMessage = `failed to delete conversation, status ${response.status}: ${response.statusText}`;\n\n try {\n const errorBody = await response.json();\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n\n throw new Error(errorMessage);\n }\n return { success: true };\n }\n\n async renameConversation(conversation_id: string, newName: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ topic_summary: newName }),\n },\n );\n\n if (!response.ok) {\n let errorMessage = `failed to rename conversation, status ${response.status}: ${response.statusText}`;\n\n try {\n const errorBody = await response.json();\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n\n throw new Error(errorMessage);\n }\n return { success: true };\n }\n\n getFeedbackStatus = async () => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/feedback/status`,\n {\n method: 'GET',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to GET feedback status, status ${response.status}: ${response.statusText}`,\n );\n }\n const result = await response.json();\n return result?.status?.enabled ?? false;\n };\n\n captureFeedback = async (payload: CaptureFeedback) => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n accept: 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(\n `failed to capture feedback, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n };\n}\n"],"names":[],"mappings":";;AAwCO,MAAM,mBAAA,CAA6C;AAAA,EACvC,SAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAAkB;AAC5B,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAA,GAAa;AACjB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,SAAA,CAAU,iBAAiB,CAAC,CAAA,eAAA,CAAA;AAAA,EACvD;AAAA,EAEA,MAAM,aAAA,CACJ,MAAA,EACA,aAAA,EACA,gBAAA,EACA,iBACA,WAAA,EACA;AACA,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AAEtC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,SAAS,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,SAAA,CAAA,EAAa;AAAA,MAChE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,eAAA,EACE,eAAA,KAAoB,oBAAA,GAChB,MAAA,GACA,eAAA;AAAA,QACN,KAAA,EAAO,aAAA;AAAA,QACP,QAAA,EAAU,gBAAA;AAAA,QACV,KAAA,EAAO,MAAA;AAAA,QACP;AAAA,OACD;AAAA,KACF,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,IAAA,EAAM;AAClB,MAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,IACzE;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,CAAK,SAAA,EAAU;AAC3C,MAAA,MAAM,MAAA,GAAS,KAAK,IAAA,EAAK;AACzB,MAAA,MAAM,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,IAAA,CAAK,CAAC,EAAE,IAAA,EAAM,OAAM,KAAM;AAClD,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,OAAO,EAAA;AAAA,QACT;AACA,QAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,MAC7B,CAAC,CAAA;AACD,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AACpC,MAAA,IAAI,cAAc,KAAA,EAAO;AACvB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,YAAA,CAAa,KAAK,CAAA,CAAE,CAAA;AAAA,MACnE;AAAA,IACF;AACA,IAAA,OAAO,QAAA,CAAS,KAAK,SAAA,EAAU;AAAA,EACjC;AAAA,EAEA,MAAc,QAAQ,GAAA,EAAa;AACjC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,MAAM,GAAA,EAAK;AAAA,MAC9C,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA;AAClB,KACD,CAAA;AACD,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,6BAAA,EAAgC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OACzE;AAAA,IACF;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA,EAEA,MAAM,yBAAA,GAA4B;AAChC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AACtC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,CAAA,EAAG,OAAO,CAAA,WAAA,CAAa,CAAA;AAEzD,IAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,8BAAA,EAAiC,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OACtE;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,EAAK;AACnC,IAAA,IAAI,CAAC,UAAU,OAAA,IAAW,CAAC,MAAM,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA,EAAG;AAC1D,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,IAAA,CAAK,CAAC,MAAA,KAAgB;AAC5C,MAAA,MAAM,aAAa,MAAA,CAAO,oBAAA;AAE1B,MAAA,OAAO,oCAAA,CAAqC,IAAA;AAAA,QAC1C,CAAA,OAAA,KAAW,UAAA,CAAW,WAAA,EAAY,KAAM,QAAQ,WAAA;AAAY,OAC9D;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,YAAA,GAAe;AACnB,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AACtC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,CAAA,EAAG,OAAO,CAAA,UAAA,CAAY,CAAA;AAExD,IAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,6BAAA,EAAgC,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OACrE;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,EAAK;AAEnC,IAAA,OAAO,QAAA,EAAU,MAAA,GAAS,QAAA,EAAU,MAAA,GAAS,EAAC;AAAA,EAChD;AAAA,EAEA,MAAM,wBAAwB,eAAA,EAAyB;AACrD,IAAA,IAAI,oBAAoB,oBAAA,EAAsB;AAC5C,MAAA,OAAO,EAAC;AAAA,IACV;AACA,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AACtC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA;AAAA,MACxB,CAAA,EAAG,OAAO,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,eAAe,CAAC,CAAA;AAAA,KACpE;AACA,IAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,4CAAA,EAA+C,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OACpF;AAAA,IACF;AACA,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,EAAK;AACnC,IAAA,OAAO,QAAA,CAAS,gBAAgB,EAAC;AAAA,EACnC;AAAA,EAEA,MAAM,gBAAA,GAAmB;AACvB,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AACtC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,CAAA,EAAG,OAAO,CAAA,iBAAA,CAAmB,CAAA;AAE/D,IAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,mCAAA,EAAsC,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OAC3E;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,MAAA,CAAO,IAAA,EAAK;AACnC,IAAA,OAAO,QAAA,CAAS,iBAAiB,EAAC;AAAA,EACpC;AAAA,EAEA,MAAM,YAAY,SAAA,EAAkD;AAClE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AACtC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,GAAG,OAAO,CAAA,mBAAA,CAAA;AAAA,MACV;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB;AAAA,SAClB;AAAA,QACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,UAAA,EAAY,WAAW;AAAA;AAChD,KACF;AACA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,+BAAA,EAAkC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OAC3E;AAAA,IACF;AACA,IAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,EAC7B;AAAA,EACA,MAAM,mBAAmB,eAAA,EAAyB;AAChD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AAEtC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,CAAA,EAAG,OAAO,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,eAAe,CAAC,CAAA,CAAA;AAAA,MAClE;AAAA,QACE,MAAA,EAAQ,QAAA;AAAA,QACR,SAAS;AAAC;AACZ,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,eAAe,CAAA,sCAAA,EAAyC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA,CAAA;AAEnG,MAAA,IAAI;AACF,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,QAAA,IAAI,WAAW,KAAA,EAAO;AACpB,UAAA,YAAA,GAAe,SAAA,CAAU,KAAA;AAAA,QAC3B;AAAA,MACF,SAAS,CAAA,EAAG;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,MAChB;AAEA,MAAA,MAAM,IAAI,MAAM,YAAY,CAAA;AAAA,IAC9B;AACA,IAAA,OAAO,EAAE,SAAS,IAAA,EAAK;AAAA,EACzB;AAAA,EAEA,MAAM,kBAAA,CAAmB,eAAA,EAAyB,OAAA,EAAiB;AACjE,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AAEtC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,CAAA,EAAG,OAAO,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,eAAe,CAAC,CAAA,CAAA;AAAA,MAClE;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB;AAAA,SAClB;AAAA,QACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,aAAA,EAAe,SAAS;AAAA;AACjD,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,eAAe,CAAA,sCAAA,EAAyC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA,CAAA;AAEnG,MAAA,IAAI;AACF,QAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,QAAA,IAAI,WAAW,KAAA,EAAO;AACpB,UAAA,YAAA,GAAe,SAAA,CAAU,KAAA;AAAA,QAC3B;AAAA,MACF,SAAS,CAAA,EAAG;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,MAChB;AAEA,MAAA,MAAM,IAAI,MAAM,YAAY,CAAA;AAAA,IAC9B;AACA,IAAA,OAAO,EAAE,SAAS,IAAA,EAAK;AAAA,EACzB;AAAA,EAEA,oBAAoB,YAAY;AAC9B,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AAEtC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,GAAG,OAAO,CAAA,mBAAA,CAAA;AAAA,MACV;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,SAAS;AAAC;AACZ,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sCAAA,EAAyC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OAClF;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,IAAA,EAAK;AACnC,IAAA,OAAO,MAAA,EAAQ,QAAQ,OAAA,IAAW,KAAA;AAAA,EACpC,CAAA;AAAA,EAEA,eAAA,GAAkB,OAAO,OAAA,KAA6B;AACpD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,UAAA,EAAW;AAEtC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,SAAS,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,YAAA,CAAA,EAAgB;AAAA,MACnE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,MAAA,EAAQ;AAAA,OACV;AAAA,MACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC7B,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,mCAAA,EAAsC,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OAC/E;AAAA,IACF;AACA,IAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAAA,EAC7B,CAAA;AACF;;;;"}
1
+ {"version":3,"file":"LightspeedApiClient.esm.js","sources":["../../src/api/LightspeedApiClient.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 { ConfigApi, FetchApi } from '@backstage/core-plugin-api';\n\nimport {\n TEMP_CONVERSATION_ID,\n VALID_TOPIC_RESTRICTION_PROVIDER_IDS,\n} from '../const';\nimport { Attachment, CaptureFeedback } from '../types';\nimport { LightspeedAPI } from './api';\n\n/**\n * @public\n * Lightspeed API client options\n */\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * Lightspeed API client implementation\n */\n\nexport class LightspeedApiClient implements LightspeedAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: Options) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n conversation_id:\n conversation_id === TEMP_CONVERSATION_ID\n ? undefined\n : conversation_id,\n model: selectedModel,\n provider: selectedProvider,\n query: prompt,\n attachments,\n }),\n });\n\n if (!response.body) {\n throw new Error('Readable stream is not supported or there is no body.');\n }\n\n if (!response.ok) {\n const body = await response.body.getReader();\n const reader = body.read();\n const decoder = new TextDecoder('utf-8');\n const text = await reader.then(({ done, value }) => {\n if (done) {\n return '';\n }\n return decoder.decode(value);\n });\n const errorMessage = JSON.parse(text);\n if (errorMessage?.error) {\n throw new Error(`failed to create message: ${errorMessage.error}`);\n }\n }\n return response.body.getReader();\n }\n\n private async fetcher(url: string) {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(\n `failed to fetch data, status ${response.status}: ${response.statusText}`,\n );\n }\n return response;\n }\n\n async isTopicRestrictionEnabled() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/shields`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get shields, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n if (!response?.shields || !Array.isArray(response.shields)) {\n return false;\n }\n\n return response.shields.some((shield: any) => {\n const providerId = shield.provider_resource_id;\n\n return VALID_TOPIC_RESTRICTION_PROVIDER_IDS.some(\n validId => providerId.toLowerCase() === validId.toLowerCase(),\n );\n });\n }\n\n async getAllModels() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/models`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get models, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n\n return response?.models ? response?.models : [];\n }\n\n async getConversationMessages(conversation_id: string) {\n if (conversation_id === TEMP_CONVERSATION_ID) {\n return [];\n }\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n );\n if (!result.ok) {\n throw new Error(\n `failed to get conversation messages, status ${result.status}: ${result.statusText}`,\n );\n }\n const response = await result.json();\n return response.chat_history ?? [];\n }\n\n async getConversations() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v2/conversations`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get conversation, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n return response.conversations ?? [];\n }\n\n async getNotebookConversationIds() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/notebook-conversation-ids`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get notebook conversation IDs, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n return response.conversation_ids ?? [];\n }\n\n async stopMessage(requestId: string): Promise<{ success: boolean }> {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/query/interrupt`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ request_id: requestId }),\n },\n );\n if (!response.ok) {\n throw new Error(\n `failed to stop message, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n }\n async deleteConversation(conversation_id: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n let errorMessage = `failed to delete conversation, status ${response.status}: ${response.statusText}`;\n\n try {\n const errorBody = await response.json();\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n\n throw new Error(errorMessage);\n }\n return { success: true };\n }\n\n async renameConversation(conversation_id: string, newName: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ topic_summary: newName }),\n },\n );\n\n if (!response.ok) {\n let errorMessage = `failed to rename conversation, status ${response.status}: ${response.statusText}`;\n\n try {\n const errorBody = await response.json();\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n\n throw new Error(errorMessage);\n }\n return { success: true };\n }\n\n getFeedbackStatus = async () => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/feedback/status`,\n {\n method: 'GET',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to GET feedback status, status ${response.status}: ${response.statusText}`,\n );\n }\n const result = await response.json();\n return result?.status?.enabled ?? false;\n };\n\n captureFeedback = async (payload: CaptureFeedback) => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n accept: 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(\n `failed to capture feedback, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.esm.js","sources":["../../src/api/api.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 { createApiRef, type ApiRef } from '@backstage/core-plugin-api';\n\nimport {\n Attachment,\n BaseMessage,\n CaptureFeedback,\n ConversationList,\n LCSModel,\n} from '../types';\n\n/**\n * @public\n * Lightspeed API\n */\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<LCSModel[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n renameConversation: (\n conversation_id: string,\n newName: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n getFeedbackStatus: () => Promise<boolean>;\n captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>;\n isTopicRestrictionEnabled: () => Promise<boolean>;\n stopMessage: (requestId: string) => Promise<{ success: boolean }>;\n};\n\n/**\n * @public\n * Lightspeed API interface\n */\n\nexport const lightspeedApiRef: ApiRef<LightspeedAPI> =\n createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n });\n"],"names":[],"mappings":";;AA4DO,MAAM,mBACX,YAAA,CAA4B;AAAA,EAC1B,EAAA,EAAI;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"api.esm.js","sources":["../../src/api/api.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 { createApiRef, type ApiRef } from '@backstage/core-plugin-api';\n\nimport {\n Attachment,\n BaseMessage,\n CaptureFeedback,\n ConversationList,\n LCSModel,\n} from '../types';\n\n/**\n * @public\n * Lightspeed API\n */\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<LCSModel[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n renameConversation: (\n conversation_id: string,\n newName: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n getNotebookConversationIds: () => Promise<string[]>;\n getFeedbackStatus: () => Promise<boolean>;\n captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>;\n isTopicRestrictionEnabled: () => Promise<boolean>;\n stopMessage: (requestId: string) => Promise<{ success: boolean }>;\n};\n\n/**\n * @public\n * Lightspeed API interface\n */\n\nexport const lightspeedApiRef: ApiRef<LightspeedAPI> =\n createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n });\n"],"names":[],"mappings":";;AA6DO,MAAM,mBACX,YAAA,CAA4B;AAAA,EAC1B,EAAA,EAAI;AACN,CAAC;;;;"}
@@ -1,14 +1,16 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { useState, useEffect, useRef, useLayoutEffect, useCallback, useMemo } from 'react';
3
3
  import { useNavigate, useMatch, useLocation } from 'react-router-dom';
4
+ import { useApi, configApiRef } from '@backstage/core-plugin-api';
4
5
  import { Button, makeStyles } from '@material-ui/core';
6
+ import Tab from '@mui/material/Tab';
7
+ import Tabs from '@mui/material/Tabs';
5
8
  import { ChatbotDisplayMode, ChatbotContent, ChatbotFooter, MessageBar, ChatbotFootnote, Settings, Chatbot, ChatbotHeader, ChatbotHeaderMain, ChatbotHeaderMenu, ChatbotHeaderTitle, FileDropZone, ChatbotAlert } from '@patternfly/chatbot';
6
9
  import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
7
- import { DropdownItem, Tooltip, MenuToggle, Select, SelectList, SelectOption, AlertGroup, Alert, AlertVariant, AlertActionCloseButton, Title, Tabs, Tab } from '@patternfly/react-core';
10
+ import { DropdownItem, Tooltip, MenuToggle, Select, SelectList, SelectOption, AlertGroup, Alert, AlertVariant, AlertActionCloseButton, Title } from '@patternfly/react-core';
8
11
  import { SortAmountDownAltIcon, SortAmountDownIcon, PlusIcon, SearchIcon } from '@patternfly/react-icons';
9
12
  import { useQueryClient } from '@tanstack/react-query';
10
13
  import { UNTITLED_NOTEBOOK_NAME, TEMP_CONVERSATION_ID, supportedFileTypes } from '../const.esm.js';
11
- import '@backstage/core-plugin-api';
12
14
  import '../api/api.esm.js';
13
15
  import { useBackstageUserIdentity } from '../hooks/useBackstageUserIdentity.esm.js';
14
16
  import { useConversationMessages } from '../hooks/useConversationMessages.esm.js';
@@ -20,6 +22,7 @@ import { useLightspeedDeletePermission } from '../hooks/useLightspeedDeletePermi
20
22
  import { useLightspeedNotebooksPermission } from '../hooks/notebooks/useLightspeedNotebooksPermission.esm.js';
21
23
  import '@backstage/plugin-permission-react';
22
24
  import '@red-hat-developer-hub/backstage-plugin-lightspeed-common';
25
+ import { useNotebookConversationIds } from '../hooks/useNotebookConversationIds.esm.js';
23
26
  import { useNotebookSession } from '../hooks/notebooks/useNotebookSession.esm.js';
24
27
  import { useNotebookSessions } from '../hooks/notebooks/useNotebookSessions.esm.js';
25
28
  import { usePinnedChatsSettings } from '../hooks/usePinnedChatsSettings.esm.js';
@@ -63,6 +66,9 @@ const useStyles = makeStyles((theme) => ({
63
66
  "& h1, & h2, & h3, & h4, & h5, & h6, & p, & li": {
64
67
  margin: 0,
65
68
  padding: 0
69
+ },
70
+ "& .pf-chatbot__content": {
71
+ backgroundColor: "var(--pf-t--global--background--color--floating--default)"
66
72
  }
67
73
  },
68
74
  header: {
@@ -105,29 +111,6 @@ const useStyles = makeStyles((theme) => ({
105
111
  fontFamily: '"Red Hat Display", sans-serif !important'
106
112
  }
107
113
  },
108
- tabs: {
109
- padding: `0 ${theme.spacing(2)}px`,
110
- backgroundColor: "var(--pf-t--global--background--color--floating--default)",
111
- "& .pf-v6-c-tabs__item, & .pf-v5-c-tabs__item": {
112
- backgroundColor: "transparent"
113
- },
114
- "& .pf-v6-c-tabs__item:not(:last-child), & .pf-v5-c-tabs__item:not(:last-child)": {
115
- marginRight: theme.spacing(5)
116
- },
117
- "& .pf-v6-c-tabs__link, & .pf-v5-c-tabs__link": {
118
- backgroundColor: "transparent",
119
- paddingTop: theme.spacing(2),
120
- paddingBottom: theme.spacing(2),
121
- fontWeight: 700,
122
- cursor: "pointer"
123
- },
124
- "& .pf-v6-c-tabs__item.pf-m-current .pf-v6-c-tabs__link, & .pf-v5-c-tabs__item.pf-m-current .pf-v5-c-tabs__link": {
125
- color: "var(--pf-t--global--text--color--brand--default)"
126
- }
127
- },
128
- tabsDivider: {
129
- borderTop: "1px solid var(--pf-t--global--border--color--default)"
130
- },
131
114
  headerDivider: {
132
115
  paddingTop: 8,
133
116
  borderBottom: "1px solid var(--pf-t--global--border--color--default)",
@@ -276,7 +259,7 @@ const useStyles = makeStyles((theme) => ({
276
259
  maxWidth: "unset !important"
277
260
  },
278
261
  "& .pf-chatbot__message-bar": {
279
- backgroundColor: theme.palette.grey[300]
262
+ backgroundColor: theme.palette.type === "light" ? theme.palette.grey[100] : "var(--pf-t--global--background--color--secondary--default)"
280
263
  }
281
264
  },
282
265
  fullscreenFooter: {
@@ -288,7 +271,6 @@ const useStyles = makeStyles((theme) => ({
288
271
  }
289
272
  },
290
273
  fullscreenMessageBar: {
291
- backgroundColor: "var(--pf-t--global--background--color--secondary--default)",
292
274
  border: "1px solid var(--pf-t--global--border--color--default)",
293
275
  borderRadius: 24,
294
276
  padding: theme.spacing(0.5),
@@ -503,9 +485,15 @@ const LightspeedChat = ({
503
485
  const classes = useStyles();
504
486
  const { t } = useTranslation();
505
487
  const navigate = useNavigate();
488
+ const configApi = useApi(configApiRef);
489
+ const notebooksEnabled = configApi.getOptionalBoolean("lightspeed.notebooks.enabled") ?? false;
506
490
  const notebooksRouteMatch = useMatch("/lightspeed/notebooks");
507
491
  const notebookViewRouteMatch = useMatch("/lightspeed/notebooks/:notebookId");
508
492
  const routeNotebookId = notebookViewRouteMatch?.params?.notebookId;
493
+ const isOnNotebookRoute = Boolean(
494
+ notebooksRouteMatch || notebookViewRouteMatch
495
+ );
496
+ const shouldShowTabs = notebooksEnabled || isOnNotebookRoute;
509
497
  const {
510
498
  displayMode,
511
499
  setDisplayMode,
@@ -541,6 +529,7 @@ const LightspeedChat = ({
541
529
  });
542
530
  const { allowed: hasNotebooksAccess, loading: notebooksPermissionLoading } = useLightspeedNotebooksPermission();
543
531
  const notebooksPermissionResolved = !notebooksPermissionLoading && hasNotebooksAccess;
532
+ const { data: notebookConversationIdsArray = [] } = useNotebookConversationIds();
544
533
  const { data: notebooks = [], refetch: refetchNotebooks } = useNotebookSessions(notebooksPermissionResolved);
545
534
  const hasNotebooks = notebooks.length > 0;
546
535
  const [openNotebookMenuId, setOpenNotebookMenuId] = useState(
@@ -596,7 +585,7 @@ const LightspeedChat = ({
596
585
  const wasStoppedByUserRef = useRef(false);
597
586
  const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } = useLastOpenedConversation(user);
598
587
  const showChatPanel = !isFullscreenMode || activeTab === 0;
599
- const showNotebooksPanel = isFullscreenMode && activeTab !== 0;
588
+ const showNotebooksPanel = (notebooksEnabled || isOnNotebookRoute) && activeTab !== 0;
600
589
  const [isChatHistoryDrawerOpen, setIsChatHistoryDrawerOpen] = useState(!isMobile && isFullscreenMode);
601
590
  useLayoutEffect(() => {
602
591
  if (!isFullscreenMode) {
@@ -622,8 +611,7 @@ const LightspeedChat = ({
622
611
  navigate,
623
612
  setShellViewTab
624
613
  ]);
625
- const handleNotebookTabSelect = (_event, tabIndex) => {
626
- const nextTab = Number(tabIndex);
614
+ const handleNotebookTabSelect = (_event, nextTab) => {
627
615
  setActiveTab(nextTab);
628
616
  setShellViewTab(nextTab);
629
617
  if (nextTab === 1) {
@@ -979,10 +967,8 @@ const LightspeedChat = ({
979
967
  ]
980
968
  );
981
969
  const notebookConversationIds = useMemo(
982
- () => new Set(
983
- notebooks.map((n) => n.metadata?.conversation_id).filter((id) => !!id)
984
- ),
985
- [notebooks]
970
+ () => new Set(notebookConversationIdsArray),
971
+ [notebookConversationIdsArray]
986
972
  );
987
973
  const chatOnlyConversations = useMemo(
988
974
  () => conversations.filter(
@@ -1606,22 +1592,63 @@ const LightspeedChat = ({
1606
1592
  )
1607
1593
  ] }),
1608
1594
  isFullscreenMode && /* @__PURE__ */ jsx("div", { className: classes.headerDivider }),
1609
- isFullscreenMode && /* @__PURE__ */ jsxs(Fragment, { children: [
1610
- /* @__PURE__ */ jsxs(
1611
- Tabs,
1612
- {
1613
- activeKey: activeTab,
1614
- onSelect: handleNotebookTabSelect,
1615
- "aria-label": t("tabs.ariaLabel"),
1616
- className: classes.tabs,
1617
- children: [
1618
- /* @__PURE__ */ jsx(Tab, { eventKey: 0, title: t("tabs.chat") }),
1619
- /* @__PURE__ */ jsx(Tab, { eventKey: 1, title: t("tabs.notebooks") })
1620
- ]
1621
- }
1622
- ),
1623
- /* @__PURE__ */ jsx("div", { className: classes.tabsDivider })
1624
- ] }),
1595
+ isFullscreenMode && shouldShowTabs && /* @__PURE__ */ jsxs(
1596
+ Tabs,
1597
+ {
1598
+ value: activeTab,
1599
+ onChange: handleNotebookTabSelect,
1600
+ "aria-label": t("tabs.ariaLabel"),
1601
+ variant: "standard",
1602
+ textColor: "primary",
1603
+ sx: (theme) => ({
1604
+ backgroundColor: "var(--pf-t--global--background--color--floating--default)",
1605
+ borderBottom: "1px solid var(--pf-t--global--border--color--default)",
1606
+ minHeight: theme.spacing(6),
1607
+ paddingLeft: theme.spacing(2),
1608
+ paddingRight: theme.spacing(2),
1609
+ '& [role="tab"]': {
1610
+ fontWeight: 700,
1611
+ textTransform: "none",
1612
+ opacity: 1,
1613
+ '&:not([aria-selected="true"])': {
1614
+ color: `${theme.palette.text.primary} !important`
1615
+ },
1616
+ "&:hover": {
1617
+ background: "none !important",
1618
+ backgroundColor: "transparent !important",
1619
+ backgroundImage: "none !important"
1620
+ },
1621
+ '&:not([aria-selected="true"]):hover': {
1622
+ color: "var(--pf-t--global--text--color--brand--default)"
1623
+ },
1624
+ '&[aria-selected="true"]:hover': {
1625
+ color: `${theme.palette.primary.main} !important`
1626
+ }
1627
+ },
1628
+ '& [class*="Tabs-indicator"]': {
1629
+ height: 3
1630
+ }
1631
+ }),
1632
+ children: [
1633
+ /* @__PURE__ */ jsx(
1634
+ Tab,
1635
+ {
1636
+ disableRipple: true,
1637
+ label: t("tabs.chat"),
1638
+ "aria-label": t("tabs.chat")
1639
+ }
1640
+ ),
1641
+ /* @__PURE__ */ jsx(
1642
+ Tab,
1643
+ {
1644
+ disableRipple: true,
1645
+ label: t("tabs.notebooks"),
1646
+ "aria-label": t("tabs.notebooks")
1647
+ }
1648
+ )
1649
+ ]
1650
+ }
1651
+ ),
1625
1652
  showChatPanel && /* @__PURE__ */ jsx(
1626
1653
  ConditionalWrapper,
1627
1654
  {
@@ -1721,7 +1748,6 @@ const LightspeedChat = ({
1721
1748
  avatar,
1722
1749
  profileLoading,
1723
1750
  topicRestrictionEnabled,
1724
- selectedModel,
1725
1751
  onClose: handleCloseNotebook
1726
1752
  }
1727
1753
  ),