@red-hat-developer-hub/backstage-plugin-lightspeed 2.4.0 → 2.5.1
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 +25 -0
- package/README.md +5 -1
- package/dist/alpha.d.ts +6 -0
- package/dist/api/NotebooksApiClient.esm.js +36 -0
- package/dist/api/NotebooksApiClient.esm.js.map +1 -1
- package/dist/api/notebooksApi.esm.js.map +1 -1
- package/dist/components/LightSpeedChat.esm.js +143 -26
- package/dist/components/LightSpeedChat.esm.js.map +1 -1
- package/dist/components/LightspeedChatBoxHeader.esm.js +2 -1
- package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
- package/dist/components/LightspeedDrawerContext.esm.js.map +1 -1
- package/dist/components/Router.esm.js +2 -1
- package/dist/components/Router.esm.js.map +1 -1
- package/dist/components/notebooks/AddDocumentModal.esm.js +122 -23
- package/dist/components/notebooks/AddDocumentModal.esm.js.map +1 -1
- package/dist/components/notebooks/DocumentSidebar.esm.js +85 -5
- package/dist/components/notebooks/DocumentSidebar.esm.js.map +1 -1
- package/dist/components/notebooks/FileListItem.esm.js +94 -0
- package/dist/components/notebooks/FileListItem.esm.js.map +1 -0
- package/dist/components/notebooks/NotebookView.esm.js +249 -45
- package/dist/components/notebooks/NotebookView.esm.js.map +1 -1
- package/dist/components/notebooks/OverwriteConfirmModal.esm.js +2 -1
- package/dist/components/notebooks/OverwriteConfirmModal.esm.js.map +1 -1
- package/dist/components/notebooks/SidebarCollapseIcon.esm.js +5 -2
- package/dist/components/notebooks/SidebarCollapseIcon.esm.js.map +1 -1
- package/dist/hooks/notebooks/useCreateNotebookMessage.esm.js +24 -0
- package/dist/hooks/notebooks/useCreateNotebookMessage.esm.js.map +1 -0
- package/dist/hooks/notebooks/useNotebookDocuments.esm.js +1 -1
- package/dist/hooks/notebooks/useNotebookDocuments.esm.js.map +1 -1
- package/dist/hooks/notebooks/useNotebookSession.esm.js +18 -0
- package/dist/hooks/notebooks/useNotebookSession.esm.js.map +1 -0
- package/dist/hooks/toolCallsCacheStore.esm.js +61 -0
- package/dist/hooks/toolCallsCacheStore.esm.js.map +1 -0
- package/dist/hooks/useConversationMessages.esm.js +21 -16
- package/dist/hooks/useConversationMessages.esm.js.map +1 -1
- package/dist/hooks/useCreateCoversationMessage.esm.js.map +1 -1
- package/dist/hooks/useDeleteConversation.esm.js +2 -0
- package/dist/hooks/useDeleteConversation.esm.js.map +1 -1
- package/dist/hooks/useLightspeedProviderState.esm.js +35 -8
- package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/plugin.esm.js +3 -2
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +6 -1
- package/dist/routes.esm.js.map +1 -1
- package/dist/translations/de.esm.js +6 -0
- package/dist/translations/de.esm.js.map +1 -1
- package/dist/translations/es.esm.js +6 -0
- package/dist/translations/es.esm.js.map +1 -1
- package/dist/translations/fr.esm.js +6 -0
- package/dist/translations/fr.esm.js.map +1 -1
- package/dist/translations/it.esm.js +6 -0
- package/dist/translations/it.esm.js.map +1 -1
- package/dist/translations/ja.esm.js +6 -0
- package/dist/translations/ja.esm.js.map +1 -1
- package/dist/translations/ref.esm.js +6 -0
- package/dist/translations/ref.esm.js.map +1 -1
- package/dist/types.esm.js.map +1 -1
- package/dist/utils/lightspeed-chatbox-utils.esm.js +1 -1
- package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
## @red-hat-developer-hub/backstage-plugin-lightspeed
|
|
2
2
|
|
|
3
|
+
## 2.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d621f93: Improved notebook upload modal and MessageBar UX.
|
|
8
|
+
- d621f93: Fixed overwrite flow to add duplicate files to the Add Document modal instead of uploading immediately. Reduced notebook delete toast timeout to 2 seconds.
|
|
9
|
+
- 5d17950: Retain Lightspeed chat and tool-call state when the chat UI remounts (for example when switching display mode between embedded and overlay), so the active thread and tool metadata are not dropped while messages are still streaming or before history refetches.
|
|
10
|
+
- @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.5.1
|
|
11
|
+
|
|
12
|
+
## 2.5.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- d9df5b8: Add notebook chat with streaming support, document management, and UI improvements.
|
|
17
|
+
- Backend: add SSE transform to normalize Responses API format to legacy streaming format so notebook chat streams token-by-token like the chat tab.
|
|
18
|
+
- Frontend: add notebook chat view with conversation messages, document sidebar with per-document delete, and topic summary display.
|
|
19
|
+
- Fix stale document list when re-opening a notebook by setting query staleTime to 0.
|
|
20
|
+
- Hide model selector on the Notebooks tab while keeping the settings ellipsis menu visible.
|
|
21
|
+
|
|
22
|
+
- 8b0cb12: Add dedicated route for individual notebook view (/lightspeed/notebooks/:notebookId).
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.5.0
|
|
27
|
+
|
|
3
28
|
## 2.4.0
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -85,7 +85,11 @@ Lightspeed is a front-end plugin that enables you to interact with any LLM serve
|
|
|
85
85
|
#### Procedure
|
|
86
86
|
|
|
87
87
|
1. Open your Backstage application and select a Lightspeed nav item from the **Navigation**.
|
|
88
|
-
2. Ask
|
|
88
|
+
2. Ask your questions to the Lightspeed chatbot.
|
|
89
|
+
|
|
90
|
+
### Display modes and chat continuity
|
|
91
|
+
|
|
92
|
+
Lightspeed supports multiple **display modes** from Settings (for example overlay, docked, embedded, and fullscreen). Switching modes can remount the chat surface; your **current conversation** and **tool-call metadata** for that thread stay with the session so the active chat is not reset. Live streaming text may not update continuously across a mode switch until the assistant response finishes loading.
|
|
89
93
|
|
|
90
94
|
### MCP servers settings
|
|
91
95
|
|
package/dist/alpha.d.ts
CHANGED
|
@@ -66,10 +66,12 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
|
|
|
66
66
|
readonly "notebook.view.upload.heading": string;
|
|
67
67
|
readonly "notebook.view.upload.action": string;
|
|
68
68
|
readonly "notebook.view.input.placeholder": string;
|
|
69
|
+
readonly "notebook.view.input.disabledTooltip": string;
|
|
69
70
|
readonly "notebook.view.sidebar.collapse": string;
|
|
70
71
|
readonly "notebook.view.sidebar.expand": string;
|
|
71
72
|
readonly "notebook.view.sidebar.resize": string;
|
|
72
73
|
readonly "notebook.view.documents.uploading": string;
|
|
74
|
+
readonly "notebook.view.documents.maxReached": string;
|
|
73
75
|
readonly "notebook.upload.success": string;
|
|
74
76
|
readonly "notebook.upload.failed": string;
|
|
75
77
|
readonly "notebook.upload.modal.title": string;
|
|
@@ -77,12 +79,16 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
|
|
|
77
79
|
readonly "notebook.upload.modal.browseButton": string;
|
|
78
80
|
readonly "notebook.upload.modal.separator": string;
|
|
79
81
|
readonly "notebook.upload.modal.infoText": string;
|
|
82
|
+
readonly "notebook.upload.modal.selectedFiles": string;
|
|
83
|
+
readonly "notebook.upload.modal.addButton": string;
|
|
84
|
+
readonly "notebook.upload.modal.removeFile": string;
|
|
80
85
|
readonly "notebook.upload.error.unsupportedType": string;
|
|
81
86
|
readonly "notebook.upload.error.fileTooLarge": string;
|
|
82
87
|
readonly "notebook.upload.error.tooManyFiles": string;
|
|
83
88
|
readonly "notebook.overwrite.modal.title": string;
|
|
84
89
|
readonly "notebook.overwrite.modal.description": string;
|
|
85
90
|
readonly "notebook.overwrite.modal.action": string;
|
|
91
|
+
readonly "notebook.document.delete": string;
|
|
86
92
|
readonly "conversation.delete.confirm.title": string;
|
|
87
93
|
readonly "conversation.delete.confirm.message": string;
|
|
88
94
|
readonly "conversation.delete.confirm.action": string;
|
|
@@ -78,6 +78,16 @@ class NotebooksApiClient {
|
|
|
78
78
|
);
|
|
79
79
|
return response?.sessions ?? [];
|
|
80
80
|
}
|
|
81
|
+
async getSession(sessionId) {
|
|
82
|
+
const baseUrl = await this.getBaseUrl();
|
|
83
|
+
const response = await this.fetchJson(
|
|
84
|
+
`${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`
|
|
85
|
+
);
|
|
86
|
+
if (!response.session) {
|
|
87
|
+
throw new Error(response.error ?? "Session not found");
|
|
88
|
+
}
|
|
89
|
+
return response.session;
|
|
90
|
+
}
|
|
81
91
|
async renameSession(sessionId, name) {
|
|
82
92
|
const baseUrl = await this.getBaseUrl();
|
|
83
93
|
await this.fetchJson(
|
|
@@ -137,6 +147,32 @@ class NotebooksApiClient {
|
|
|
137
147
|
`${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}/status`
|
|
138
148
|
);
|
|
139
149
|
}
|
|
150
|
+
async querySession(sessionId, query) {
|
|
151
|
+
const baseUrl = await this.getBaseUrl();
|
|
152
|
+
const response = await this.fetchApi.fetch(
|
|
153
|
+
`${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/query`,
|
|
154
|
+
{
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: { "Content-Type": "application/json" },
|
|
157
|
+
body: JSON.stringify({ query })
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
if (!response.body) {
|
|
161
|
+
throw new Error("Readable stream is not supported or there is no body.");
|
|
162
|
+
}
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
const reader = response.body.getReader();
|
|
165
|
+
const { done, value } = await reader.read();
|
|
166
|
+
const text = done ? "" : new TextDecoder("utf-8").decode(value);
|
|
167
|
+
const errorMessage = JSON.parse(text);
|
|
168
|
+
if (errorMessage?.error) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`failed to query notebook session: ${errorMessage.error}`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return response.body.getReader();
|
|
175
|
+
}
|
|
140
176
|
}
|
|
141
177
|
|
|
142
178
|
export { NotebooksApiClient };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotebooksApiClient.esm.js","sources":["../../src/api/NotebooksApiClient.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 DocumentListResponse,\n DocumentStatus,\n NotebookSession,\n SessionResponse,\n UploadDocumentResponse,\n} from '../types';\nimport { NotebooksAPI } from './notebooksApi';\n\n/**\n * @public\n * AI Notebooks API client options\n */\nexport type NotebooksOptions = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * AI Notebooks API client implementation\n */\nexport class NotebooksApiClient implements NotebooksAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: NotebooksOptions) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed/notebooks`;\n }\n\n private async handleResponseError(response: Response): Promise<string> {\n let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;\n try {\n const errorText = await response.text();\n if (errorText) {\n try {\n const errorBody = JSON.parse(errorText);\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch {\n errorMessage = errorText;\n }\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n return errorMessage;\n }\n\n private async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n ...init,\n });\n\n if (!response.ok) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n private async fetchFormData<T>(\n url: string,\n formData: FormData,\n method: string = 'PUT',\n ): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n method,\n body: formData,\n });\n\n if (!response.ok && response.status !== 202) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n async createSession(name: string, description?: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<SessionResponse>(\n `${baseUrl}/v1/sessions`,\n {\n method: 'POST',\n body: JSON.stringify({ name, description }),\n },\n );\n if (!response.session) {\n throw new Error(response.error ?? 'Failed to create session');\n }\n return response.session;\n }\n\n async listSessions() {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<{ sessions?: NotebookSession[] }>(\n `${baseUrl}/v1/sessions`,\n );\n return response?.sessions ?? [];\n }\n\n async renameSession(sessionId: string, name: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ name }),\n },\n );\n }\n\n async deleteSession(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async uploadDocument(\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) {\n const baseUrl = await this.getBaseUrl();\n const formData = new FormData();\n formData.append('file', file);\n formData.append('fileType', fileType);\n formData.append('title', title);\n if (newTitle) {\n formData.append('newTitle', newTitle);\n }\n return this.fetchFormData<UploadDocumentResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n formData,\n );\n }\n\n async listDocuments(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<DocumentListResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n );\n const docs = response?.documents ?? [];\n return docs.map(doc => ({\n ...doc,\n title: (doc as any).title ?? doc.document_id,\n }));\n }\n\n async deleteDocument(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async getDocumentStatus(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n return this.fetchJson<DocumentStatus>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}/status`,\n );\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NotebooksApiClient.esm.js","sources":["../../src/api/NotebooksApiClient.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 DocumentListResponse,\n DocumentStatus,\n NotebookSession,\n SessionResponse,\n UploadDocumentResponse,\n} from '../types';\nimport { NotebooksAPI } from './notebooksApi';\n\n/**\n * @public\n * AI Notebooks API client options\n */\nexport type NotebooksOptions = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * AI Notebooks API client implementation\n */\nexport class NotebooksApiClient implements NotebooksAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: NotebooksOptions) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed/notebooks`;\n }\n\n private async handleResponseError(response: Response): Promise<string> {\n let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;\n try {\n const errorText = await response.text();\n if (errorText) {\n try {\n const errorBody = JSON.parse(errorText);\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch {\n errorMessage = errorText;\n }\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n return errorMessage;\n }\n\n private async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n ...init,\n });\n\n if (!response.ok) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n private async fetchFormData<T>(\n url: string,\n formData: FormData,\n method: string = 'PUT',\n ): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n method,\n body: formData,\n });\n\n if (!response.ok && response.status !== 202) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n async createSession(name: string, description?: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<SessionResponse>(\n `${baseUrl}/v1/sessions`,\n {\n method: 'POST',\n body: JSON.stringify({ name, description }),\n },\n );\n if (!response.session) {\n throw new Error(response.error ?? 'Failed to create session');\n }\n return response.session;\n }\n\n async listSessions() {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<{ sessions?: NotebookSession[] }>(\n `${baseUrl}/v1/sessions`,\n );\n return response?.sessions ?? [];\n }\n\n async getSession(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<SessionResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n );\n if (!response.session) {\n throw new Error(response.error ?? 'Session not found');\n }\n return response.session;\n }\n\n async renameSession(sessionId: string, name: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ name }),\n },\n );\n }\n\n async deleteSession(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async uploadDocument(\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) {\n const baseUrl = await this.getBaseUrl();\n const formData = new FormData();\n formData.append('file', file);\n formData.append('fileType', fileType);\n formData.append('title', title);\n if (newTitle) {\n formData.append('newTitle', newTitle);\n }\n return this.fetchFormData<UploadDocumentResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n formData,\n );\n }\n\n async listDocuments(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<DocumentListResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n );\n const docs = response?.documents ?? [];\n return docs.map(doc => ({\n ...doc,\n title: (doc as any).title ?? doc.document_id,\n }));\n }\n\n async deleteDocument(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async getDocumentStatus(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n return this.fetchJson<DocumentStatus>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}/status`,\n );\n }\n\n async querySession(\n sessionId: string,\n query: string,\n ): Promise<ReadableStreamDefaultReader<Uint8Array>> {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/query`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ query }),\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 reader = response.body.getReader();\n const { done, value } = await reader.read();\n const text = done ? '' : new TextDecoder('utf-8').decode(value);\n const errorMessage = JSON.parse(text);\n if (errorMessage?.error) {\n throw new Error(\n `failed to query notebook session: ${errorMessage.error}`,\n );\n }\n }\n\n return response.body.getReader();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notebooksApi.esm.js","sources":["../../src/api/notebooksApi.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 DocumentStatus,\n NotebookSession,\n SessionDocument,\n UploadDocumentResponse,\n} from '../types';\n\n/**\n * @public\n * AI Notebooks API\n */\nexport type NotebooksAPI = {\n createSession: (\n name: string,\n description?: string,\n ) => Promise<NotebookSession>;\n listSessions: () => Promise<NotebookSession[]>;\n renameSession: (sessionId: string, name: string) => Promise<void>;\n deleteSession: (sessionId: string) => Promise<void>;\n uploadDocument: (\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) => Promise<UploadDocumentResponse>;\n listDocuments: (sessionId: string) => Promise<SessionDocument[]>;\n deleteDocument: (sessionId: string, documentId: string) => Promise<void>;\n getDocumentStatus: (\n sessionId: string,\n documentId: string,\n ) => Promise<DocumentStatus>;\n};\n\n/**\n * @public\n * AI Notebooks API interface\n */\nexport const notebooksApiRef: ApiRef<NotebooksAPI> = createApiRef<NotebooksAPI>(\n {\n id: 'plugin.lightspeed.notebooks.service',\n },\n);\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"notebooksApi.esm.js","sources":["../../src/api/notebooksApi.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 DocumentStatus,\n NotebookSession,\n SessionDocument,\n UploadDocumentResponse,\n} from '../types';\n\n/**\n * @public\n * AI Notebooks API\n */\nexport type NotebooksAPI = {\n createSession: (\n name: string,\n description?: string,\n ) => Promise<NotebookSession>;\n listSessions: () => Promise<NotebookSession[]>;\n getSession: (sessionId: string) => Promise<NotebookSession>;\n renameSession: (sessionId: string, name: string) => Promise<void>;\n deleteSession: (sessionId: string) => Promise<void>;\n uploadDocument: (\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) => Promise<UploadDocumentResponse>;\n listDocuments: (sessionId: string) => Promise<SessionDocument[]>;\n deleteDocument: (sessionId: string, documentId: string) => Promise<void>;\n getDocumentStatus: (\n sessionId: string,\n documentId: string,\n ) => Promise<DocumentStatus>;\n querySession: (\n sessionId: string,\n query: string,\n ) => Promise<ReadableStreamDefaultReader<Uint8Array>>;\n};\n\n/**\n * @public\n * AI Notebooks API interface\n */\nexport const notebooksApiRef: ApiRef<NotebooksAPI> = createApiRef<NotebooksAPI>(\n {\n id: 'plugin.lightspeed.notebooks.service',\n },\n);\n"],"names":[],"mappings":";;AA6DO,MAAM,eAAwC,GAAA,YAAA;AAAA,EACnD;AAAA,IACE,EAAI,EAAA;AAAA;AAER;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useRef, useCallback,
|
|
2
|
+
import { useState, useEffect, useRef, useCallback, useLayoutEffect, useMemo } from 'react';
|
|
3
3
|
import { useNavigate, useMatch } from 'react-router-dom';
|
|
4
4
|
import { makeStyles, Button } from '@material-ui/core';
|
|
5
5
|
import { ChatbotDisplayMode, ChatbotContent, ChatbotFooter, MessageBar, ChatbotFootnote, Settings, Chatbot, ChatbotHeader, ChatbotHeaderMain, ChatbotHeaderMenu, ChatbotHeaderTitle, FileDropZone, ChatbotAlert } from '@patternfly/chatbot';
|
|
@@ -20,6 +20,7 @@ import { useLightspeedDeletePermission } from '../hooks/useLightspeedDeletePermi
|
|
|
20
20
|
import { useLightspeedNotebooksPermission } from '../hooks/notebooks/useLightspeedNotebooksPermission.esm.js';
|
|
21
21
|
import '@backstage/plugin-permission-react';
|
|
22
22
|
import '@red-hat-developer-hub/backstage-plugin-lightspeed-common';
|
|
23
|
+
import { useNotebookSession } from '../hooks/notebooks/useNotebookSession.esm.js';
|
|
23
24
|
import { useNotebookSessions } from '../hooks/notebooks/useNotebookSessions.esm.js';
|
|
24
25
|
import { usePinnedChatsSettings } from '../hooks/usePinnedChatsSettings.esm.js';
|
|
25
26
|
import { useSortSettings } from '../hooks/useSortSettings.esm.js';
|
|
@@ -381,15 +382,17 @@ const LightspeedChat = ({
|
|
|
381
382
|
const { t } = useTranslation();
|
|
382
383
|
const navigate = useNavigate();
|
|
383
384
|
const notebooksRouteMatch = useMatch("/lightspeed/notebooks");
|
|
385
|
+
const notebookViewRouteMatch = useMatch("/lightspeed/notebooks/:notebookId");
|
|
386
|
+
const routeNotebookId = notebookViewRouteMatch?.params?.notebookId;
|
|
384
387
|
const user = useBackstageUserIdentity();
|
|
385
388
|
const [filterValue, setFilterValue] = useState("");
|
|
386
389
|
const [announcement, setAnnouncement] = useState("");
|
|
387
390
|
const [activeTab, setActiveTab] = useState(
|
|
388
|
-
notebooksRouteMatch ? 1 : 0
|
|
391
|
+
notebooksRouteMatch || notebookViewRouteMatch ? 1 : 0
|
|
389
392
|
);
|
|
390
393
|
const { allowed: hasNotebooksAccess, loading: notebooksPermissionLoading } = useLightspeedNotebooksPermission();
|
|
391
394
|
const notebooksPermissionResolved = !notebooksPermissionLoading && hasNotebooksAccess;
|
|
392
|
-
const { data: notebooks = [], refetch: refetchNotebooks } = useNotebookSessions(
|
|
395
|
+
const { data: notebooks = [], refetch: refetchNotebooks } = useNotebookSessions(notebooksPermissionResolved);
|
|
393
396
|
const hasNotebooks = notebooks.length > 0;
|
|
394
397
|
const [openNotebookMenuId, setOpenNotebookMenuId] = useState(
|
|
395
398
|
null
|
|
@@ -399,6 +402,27 @@ const LightspeedChat = ({
|
|
|
399
402
|
const [activeNotebook, setActiveNotebook] = useState(
|
|
400
403
|
null
|
|
401
404
|
);
|
|
405
|
+
const {
|
|
406
|
+
data: routeNotebook,
|
|
407
|
+
isLoading: routeNotebookLoading,
|
|
408
|
+
isError: routeNotebookError
|
|
409
|
+
} = useNotebookSession(routeNotebookId);
|
|
410
|
+
useEffect(() => {
|
|
411
|
+
if (routeNotebookId && routeNotebook && !routeNotebookLoading) {
|
|
412
|
+
setActiveNotebook(routeNotebook);
|
|
413
|
+
} else if (routeNotebookId && routeNotebookError) {
|
|
414
|
+
navigate("/lightspeed/notebooks", { replace: true });
|
|
415
|
+
} else if (!routeNotebookId && notebooksRouteMatch) {
|
|
416
|
+
setActiveNotebook(null);
|
|
417
|
+
}
|
|
418
|
+
}, [
|
|
419
|
+
routeNotebookId,
|
|
420
|
+
routeNotebook,
|
|
421
|
+
routeNotebookLoading,
|
|
422
|
+
routeNotebookError,
|
|
423
|
+
notebooksRouteMatch,
|
|
424
|
+
navigate
|
|
425
|
+
]);
|
|
402
426
|
const [notebookAlerts, setNotebookAlerts] = useState(
|
|
403
427
|
[]
|
|
404
428
|
);
|
|
@@ -428,7 +452,8 @@ const LightspeedChat = ({
|
|
|
428
452
|
currentConversationId: routeConversationId,
|
|
429
453
|
setCurrentConversationId,
|
|
430
454
|
draftMessage,
|
|
431
|
-
setDraftMessage
|
|
455
|
+
setDraftMessage,
|
|
456
|
+
consumePendingOverlayThreadHandoff
|
|
432
457
|
} = useLightspeedDrawerContext();
|
|
433
458
|
const isFullscreenMode = displayMode === ChatbotDisplayMode.embedded;
|
|
434
459
|
const showChatPanel = !isFullscreenMode || activeTab === 0;
|
|
@@ -453,15 +478,15 @@ const LightspeedChat = ({
|
|
|
453
478
|
{ name: UNTITLED_NOTEBOOK_NAME },
|
|
454
479
|
{
|
|
455
480
|
onSuccess: (session) => {
|
|
456
|
-
|
|
481
|
+
navigate(`/lightspeed/notebooks/${session.session_id}`);
|
|
457
482
|
}
|
|
458
483
|
}
|
|
459
484
|
);
|
|
460
|
-
}, [createNotebookMutation]);
|
|
485
|
+
}, [createNotebookMutation, navigate]);
|
|
461
486
|
const handleCloseNotebook = useCallback(() => {
|
|
462
|
-
|
|
487
|
+
navigate("/lightspeed/notebooks");
|
|
463
488
|
refetchNotebooks();
|
|
464
|
-
}, [refetchNotebooks]);
|
|
489
|
+
}, [navigate, refetchNotebooks]);
|
|
465
490
|
const handleRemoveNotebookAlert = (key) => {
|
|
466
491
|
setNotebookAlerts(
|
|
467
492
|
(prevAlerts) => prevAlerts.filter((alert) => alert.key !== key)
|
|
@@ -505,11 +530,44 @@ const LightspeedChat = ({
|
|
|
505
530
|
handleFileUpload,
|
|
506
531
|
setUploadError
|
|
507
532
|
} = useFileAttachmentContext();
|
|
533
|
+
useLayoutEffect(() => {
|
|
534
|
+
if (!isReady) return;
|
|
535
|
+
if (isFullscreenMode) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const handoff = consumePendingOverlayThreadHandoff?.() ?? false;
|
|
539
|
+
if (handoff) {
|
|
540
|
+
if (routeConversationId) {
|
|
541
|
+
setConversationId(routeConversationId);
|
|
542
|
+
setNewChatCreated(false);
|
|
543
|
+
} else {
|
|
544
|
+
setConversationId(TEMP_CONVERSATION_ID);
|
|
545
|
+
setNewChatCreated(true);
|
|
546
|
+
}
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (routeConversationId && routeConversationId !== conversationId) {
|
|
550
|
+
setConversationId(routeConversationId);
|
|
551
|
+
setNewChatCreated(false);
|
|
552
|
+
} else if (!routeConversationId && conversationId === "" && lastOpenedId !== null) {
|
|
553
|
+
setConversationId(lastOpenedId);
|
|
554
|
+
}
|
|
555
|
+
}, [
|
|
556
|
+
isReady,
|
|
557
|
+
isFullscreenMode,
|
|
558
|
+
routeConversationId,
|
|
559
|
+
conversationId,
|
|
560
|
+
lastOpenedId,
|
|
561
|
+
consumePendingOverlayThreadHandoff
|
|
562
|
+
]);
|
|
508
563
|
useEffect(() => {
|
|
509
|
-
if (isReady
|
|
564
|
+
if (!isReady || !isFullscreenMode) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (lastOpenedId !== null) {
|
|
510
568
|
setConversationId(lastOpenedId);
|
|
511
569
|
}
|
|
512
|
-
}, [lastOpenedId, isReady]);
|
|
570
|
+
}, [lastOpenedId, isReady, isFullscreenMode]);
|
|
513
571
|
const queryClient = useQueryClient();
|
|
514
572
|
const {
|
|
515
573
|
data: conversations = [],
|
|
@@ -521,27 +579,53 @@ const LightspeedChat = ({
|
|
|
521
579
|
const samplePrompts = useWelcomePrompts();
|
|
522
580
|
useEffect(() => {
|
|
523
581
|
if (!user || !isReady) return;
|
|
582
|
+
const onOverlayLikeSurface = isFullscreenMode || !routeConversationId;
|
|
583
|
+
const stillOnProvisionalThread = !conversationId || conversationId === TEMP_CONVERSATION_ID;
|
|
524
584
|
if (lastOpenedId === null) {
|
|
525
|
-
|
|
585
|
+
if (onOverlayLikeSurface && stillOnProvisionalThread) {
|
|
586
|
+
setConversationId(TEMP_CONVERSATION_ID);
|
|
587
|
+
}
|
|
526
588
|
}
|
|
527
589
|
if (lastOpenedId === TEMP_CONVERSATION_ID || lastOpenedId === null) {
|
|
528
|
-
|
|
590
|
+
if (onOverlayLikeSurface && stillOnProvisionalThread) {
|
|
591
|
+
setNewChatCreated(true);
|
|
592
|
+
}
|
|
529
593
|
}
|
|
530
|
-
}, [
|
|
594
|
+
}, [
|
|
595
|
+
user,
|
|
596
|
+
isReady,
|
|
597
|
+
lastOpenedId,
|
|
598
|
+
setConversationId,
|
|
599
|
+
isFullscreenMode,
|
|
600
|
+
routeConversationId,
|
|
601
|
+
conversationId
|
|
602
|
+
]);
|
|
531
603
|
useEffect(() => {
|
|
532
604
|
if (!isLoading && !isRefetching && conversations.length === 0 && lastOpenedId) {
|
|
533
605
|
clearLastOpenedId();
|
|
606
|
+
setConversationId(TEMP_CONVERSATION_ID);
|
|
607
|
+
setNewChatCreated(true);
|
|
608
|
+
setCurrentConversationId(undefined);
|
|
534
609
|
}
|
|
535
|
-
}, [
|
|
610
|
+
}, [
|
|
611
|
+
isLoading,
|
|
612
|
+
isRefetching,
|
|
613
|
+
conversations,
|
|
614
|
+
lastOpenedId,
|
|
615
|
+
clearLastOpenedId,
|
|
616
|
+
setCurrentConversationId
|
|
617
|
+
]);
|
|
536
618
|
useEffect(() => {
|
|
537
619
|
if (!isLoading && !isRefetching && routeConversationId && isFullscreenMode) {
|
|
538
620
|
const conversationExists = conversations.some(
|
|
539
621
|
(c) => c.conversation_id === routeConversationId
|
|
540
622
|
);
|
|
541
623
|
if (!conversationExists) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
624
|
+
if (routeConversationId !== conversationId) {
|
|
625
|
+
setConversationId(TEMP_CONVERSATION_ID);
|
|
626
|
+
setCurrentConversationId(undefined);
|
|
627
|
+
setNewChatCreated(true);
|
|
628
|
+
}
|
|
545
629
|
} else if (conversationId !== routeConversationId) {
|
|
546
630
|
setConversationId(routeConversationId);
|
|
547
631
|
}
|
|
@@ -561,6 +645,10 @@ const LightspeedChat = ({
|
|
|
561
645
|
setLastOpenedId(conversationId);
|
|
562
646
|
}
|
|
563
647
|
}, [conversationId, setLastOpenedId]);
|
|
648
|
+
const viewConversationId = useMemo(
|
|
649
|
+
() => !isFullscreenMode && routeConversationId ? routeConversationId : conversationId,
|
|
650
|
+
[isFullscreenMode, routeConversationId, conversationId]
|
|
651
|
+
);
|
|
564
652
|
const onStart = (conv_id) => {
|
|
565
653
|
setConversationId(conv_id);
|
|
566
654
|
setCurrentConversationId(conv_id);
|
|
@@ -578,25 +666,26 @@ const LightspeedChat = ({
|
|
|
578
666
|
queryKey: ["conversations"]
|
|
579
667
|
});
|
|
580
668
|
queryClient.invalidateQueries({
|
|
581
|
-
queryKey: ["conversationMessages",
|
|
669
|
+
queryKey: ["conversationMessages", viewConversationId]
|
|
582
670
|
});
|
|
583
671
|
setNewChatCreated(false);
|
|
584
672
|
};
|
|
585
673
|
const { conversationMessages, handleInputPrompt, scrollToBottomRef } = useConversationMessages(
|
|
586
|
-
|
|
674
|
+
viewConversationId,
|
|
587
675
|
userName,
|
|
588
676
|
selectedModel,
|
|
589
677
|
selectedProvider,
|
|
590
678
|
avatar,
|
|
591
679
|
onComplete,
|
|
592
680
|
onStart,
|
|
681
|
+
undefined,
|
|
593
682
|
onRequestIdReady
|
|
594
683
|
);
|
|
595
684
|
const [messages, setMessages] = useState(conversationMessages);
|
|
596
685
|
const sendMessage = (message) => {
|
|
597
686
|
if (!message.toString().trim()) return;
|
|
598
687
|
wasStoppedByUserRef.current = false;
|
|
599
|
-
if (
|
|
688
|
+
if (viewConversationId !== TEMP_CONVERSATION_ID) {
|
|
600
689
|
setNewChatCreated(false);
|
|
601
690
|
}
|
|
602
691
|
setAnnouncement(
|
|
@@ -700,15 +789,33 @@ const LightspeedChat = ({
|
|
|
700
789
|
unpinChat
|
|
701
790
|
]
|
|
702
791
|
);
|
|
792
|
+
const notebookConversationIds = useMemo(
|
|
793
|
+
() => new Set(
|
|
794
|
+
notebooks.map((n) => n.metadata?.conversation_id).filter((id) => !!id)
|
|
795
|
+
),
|
|
796
|
+
[notebooks]
|
|
797
|
+
);
|
|
798
|
+
const chatOnlyConversations = useMemo(
|
|
799
|
+
() => conversations.filter(
|
|
800
|
+
(c) => !notebookConversationIds.has(c.conversation_id)
|
|
801
|
+
),
|
|
802
|
+
[conversations, notebookConversationIds]
|
|
803
|
+
);
|
|
703
804
|
const categorizedMessages = useMemo(
|
|
704
805
|
() => getCategorizeMessages(
|
|
705
|
-
|
|
806
|
+
chatOnlyConversations,
|
|
706
807
|
pinnedChats,
|
|
707
808
|
additionalMessageProps,
|
|
708
809
|
t,
|
|
709
810
|
selectedSort
|
|
710
811
|
),
|
|
711
|
-
[
|
|
812
|
+
[
|
|
813
|
+
additionalMessageProps,
|
|
814
|
+
chatOnlyConversations,
|
|
815
|
+
pinnedChats,
|
|
816
|
+
t,
|
|
817
|
+
selectedSort
|
|
818
|
+
]
|
|
712
819
|
);
|
|
713
820
|
const filterConversations = useCallback(
|
|
714
821
|
(targetValue) => {
|
|
@@ -796,7 +903,7 @@ const LightspeedChat = ({
|
|
|
796
903
|
]
|
|
797
904
|
);
|
|
798
905
|
const conversationFound = !!conversations.find(
|
|
799
|
-
(c) => c.conversation_id ===
|
|
906
|
+
(c) => c.conversation_id === viewConversationId
|
|
800
907
|
);
|
|
801
908
|
const getMaxPrompts = () => {
|
|
802
909
|
if (isFullscreenMode) {
|
|
@@ -1174,7 +1281,7 @@ const LightspeedChat = ({
|
|
|
1174
1281
|
variant: AlertVariant[variant ?? "success"],
|
|
1175
1282
|
title,
|
|
1176
1283
|
className: classes.toastAlert,
|
|
1177
|
-
timeout:
|
|
1284
|
+
timeout: 2e3,
|
|
1178
1285
|
onTimeout: () => handleRemoveNotebookAlert(key),
|
|
1179
1286
|
actionClose: /* @__PURE__ */ jsx(
|
|
1180
1287
|
AlertActionCloseButton,
|
|
@@ -1257,6 +1364,7 @@ const LightspeedChat = ({
|
|
|
1257
1364
|
models,
|
|
1258
1365
|
isPinningChatsEnabled,
|
|
1259
1366
|
isModelSelectorDisabled: isSendButtonDisabled,
|
|
1367
|
+
hideModelSelector: showNotebooksPanel,
|
|
1260
1368
|
setDisplayMode,
|
|
1261
1369
|
displayMode,
|
|
1262
1370
|
onPinnedChatsToggle: handlePinningChatsToggle,
|
|
@@ -1298,7 +1406,7 @@ const LightspeedChat = ({
|
|
|
1298
1406
|
"aria-label": t("aria.closeDrawerPanel")
|
|
1299
1407
|
},
|
|
1300
1408
|
setIsDrawerOpen: setIsChatHistoryDrawerOpen,
|
|
1301
|
-
activeItemId:
|
|
1409
|
+
activeItemId: viewConversationId,
|
|
1302
1410
|
onSelectActiveItem,
|
|
1303
1411
|
conversations: filterConversations(filterValue),
|
|
1304
1412
|
onNewChat: newChatCreated ? undefined : onNewChat,
|
|
@@ -1354,6 +1462,15 @@ const LightspeedChat = ({
|
|
|
1354
1462
|
sessionId: activeNotebook.session_id,
|
|
1355
1463
|
notebookName: activeNotebook.name,
|
|
1356
1464
|
documents: notebookDocuments,
|
|
1465
|
+
metadata: activeNotebook.metadata,
|
|
1466
|
+
topicSummary: conversations.find(
|
|
1467
|
+
(c) => c.conversation_id === activeNotebook.metadata?.conversation_id
|
|
1468
|
+
)?.topic_summary ?? undefined,
|
|
1469
|
+
userName,
|
|
1470
|
+
avatar,
|
|
1471
|
+
profileLoading,
|
|
1472
|
+
topicRestrictionEnabled,
|
|
1473
|
+
selectedModel,
|
|
1357
1474
|
onClose: handleCloseNotebook
|
|
1358
1475
|
}
|
|
1359
1476
|
),
|
|
@@ -1365,7 +1482,7 @@ const LightspeedChat = ({
|
|
|
1365
1482
|
classes,
|
|
1366
1483
|
openNotebookMenuId,
|
|
1367
1484
|
setOpenNotebookMenuId,
|
|
1368
|
-
onSelectNotebook:
|
|
1485
|
+
onSelectNotebook: (notebook) => navigate(`/lightspeed/notebooks/${notebook.session_id}`),
|
|
1369
1486
|
onRename: setRenameNotebookId,
|
|
1370
1487
|
onDelete: setDeleteNotebookId,
|
|
1371
1488
|
onCreateNotebook: handleCreateNotebook,
|