@red-hat-developer-hub/backstage-plugin-lightspeed 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +59 -0
  3. package/dist/alpha/LightspeedFAB.esm.js +69 -0
  4. package/dist/alpha/LightspeedFAB.esm.js.map +1 -0
  5. package/dist/alpha/LightspeedProvider.esm.js +6 -0
  6. package/dist/alpha/LightspeedProvider.esm.js.map +1 -0
  7. package/dist/alpha.d.ts +170 -1
  8. package/dist/alpha.esm.js +97 -1
  9. package/dist/alpha.esm.js.map +1 -1
  10. package/dist/api/LightspeedApiClient.esm.js +19 -0
  11. package/dist/api/LightspeedApiClient.esm.js.map +1 -1
  12. package/dist/api/NotebooksApiClient.esm.js +72 -0
  13. package/dist/api/NotebooksApiClient.esm.js.map +1 -0
  14. package/dist/api/api.esm.js.map +1 -1
  15. package/dist/api/notebooksApi.esm.js +10 -0
  16. package/dist/api/notebooksApi.esm.js.map +1 -0
  17. package/dist/components/DeleteModal.esm.js +2 -0
  18. package/dist/components/DeleteModal.esm.js.map +1 -1
  19. package/dist/components/LightSpeedChat.esm.js +714 -66
  20. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  21. package/dist/components/LightspeedChatBox.esm.js +18 -4
  22. package/dist/components/LightspeedChatBox.esm.js.map +1 -1
  23. package/dist/components/LightspeedChatBoxHeader.esm.js +59 -50
  24. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  25. package/dist/components/LightspeedChatContainer.esm.js +26 -2
  26. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  27. package/dist/components/LightspeedDrawerProvider.esm.js +12 -157
  28. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
  29. package/dist/components/LightspeedFAB.esm.js +5 -4
  30. package/dist/components/LightspeedFAB.esm.js.map +1 -1
  31. package/dist/components/McpServersSettings.esm.js +932 -0
  32. package/dist/components/McpServersSettings.esm.js.map +1 -0
  33. package/dist/components/McpSettingsIcon.esm.js +26 -0
  34. package/dist/components/McpSettingsIcon.esm.js.map +1 -0
  35. package/dist/components/PermissionRequiredState.esm.js +18 -22
  36. package/dist/components/PermissionRequiredState.esm.js.map +1 -1
  37. package/dist/components/RenameConversationModal.esm.js +2 -2
  38. package/dist/components/RenameConversationModal.esm.js.map +1 -1
  39. package/dist/components/ToolCallContent.esm.js +14 -4
  40. package/dist/components/ToolCallContent.esm.js.map +1 -1
  41. package/dist/components/notebooks/DeleteNotebookModal.esm.js +143 -0
  42. package/dist/components/notebooks/DeleteNotebookModal.esm.js.map +1 -0
  43. package/dist/components/notebooks/NotebookCard.esm.js +96 -0
  44. package/dist/components/notebooks/NotebookCard.esm.js.map +1 -0
  45. package/dist/components/notebooks/NotebooksTab.esm.js +61 -0
  46. package/dist/components/notebooks/NotebooksTab.esm.js.map +1 -0
  47. package/dist/components/notebooks/RenameNotebookModal.esm.js +187 -0
  48. package/dist/components/notebooks/RenameNotebookModal.esm.js.map +1 -0
  49. package/dist/const.esm.js +3 -1
  50. package/dist/const.esm.js.map +1 -1
  51. package/dist/hooks/notebooks/useDeleteNotebook.esm.js +19 -0
  52. package/dist/hooks/notebooks/useDeleteNotebook.esm.js.map +1 -0
  53. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js +15 -0
  54. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js.map +1 -0
  55. package/dist/hooks/notebooks/useNotebookSessions.esm.js +18 -0
  56. package/dist/hooks/notebooks/useNotebookSessions.esm.js.map +1 -0
  57. package/dist/hooks/notebooks/useRenameNotebook.esm.js +19 -0
  58. package/dist/hooks/notebooks/useRenameNotebook.esm.js.map +1 -0
  59. package/dist/hooks/useConversationMessages.esm.js +136 -36
  60. package/dist/hooks/useConversationMessages.esm.js.map +1 -1
  61. package/dist/hooks/useConversations.esm.js +6 -0
  62. package/dist/hooks/useConversations.esm.js.map +1 -1
  63. package/dist/hooks/useCreateCoversationMessage.esm.js.map +1 -1
  64. package/dist/hooks/useLightspeedProviderState.esm.js +205 -0
  65. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -0
  66. package/dist/hooks/useStopConversation.esm.js +18 -0
  67. package/dist/hooks/useStopConversation.esm.js.map +1 -0
  68. package/dist/hooks/useWelcomePrompts.esm.js +2 -2
  69. package/dist/hooks/useWelcomePrompts.esm.js.map +1 -1
  70. package/dist/index.d.ts +31 -31
  71. package/dist/plugin.esm.js +10 -0
  72. package/dist/plugin.esm.js.map +1 -1
  73. package/dist/translations/de.esm.js +78 -0
  74. package/dist/translations/de.esm.js.map +1 -1
  75. package/dist/translations/es.esm.js +78 -0
  76. package/dist/translations/es.esm.js.map +1 -1
  77. package/dist/translations/fr.esm.js +64 -1
  78. package/dist/translations/fr.esm.js.map +1 -1
  79. package/dist/translations/it.esm.js +66 -1
  80. package/dist/translations/it.esm.js.map +1 -1
  81. package/dist/translations/ja.esm.js +66 -1
  82. package/dist/translations/ja.esm.js.map +1 -1
  83. package/dist/translations/ref.esm.js +65 -1
  84. package/dist/translations/ref.esm.js.map +1 -1
  85. package/dist/types.esm.js.map +1 -1
  86. package/dist/utils/formatToolResponseForMarkdown.esm.js +46 -0
  87. package/dist/utils/formatToolResponseForMarkdown.esm.js.map +1 -0
  88. package/dist/utils/lightspeed-chatbox-utils.esm.js +7 -3
  89. package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
  90. package/dist/utils/notebooks-utils.esm.js +25 -0
  91. package/dist/utils/notebooks-utils.esm.js.map +1 -0
  92. package/package.json +20 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  ## @red-hat-developer-hub/backstage-plugin-lightspeed
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6ae51d4: migrated plugin to nfs
8
+ - f20f9f3: Add AI notebooks UI, hooks, and backend support for listing/renaming/deleting sessions.
9
+ - 0277bd0: Added the MCP servers selector/settings feature in Lightspeed with backend
10
+ integration for listing servers, per-user token updates, and validation.
11
+
12
+ In the settings panel, users can review server status, enable or disable
13
+ eligible servers, configure personal tokens, and get inline token validation
14
+ feedback. Token validation now runs automatically after typing stops and shows
15
+ success (`Connection successful`) or error (`Authorization failed. Try again.`)
16
+ before save.
17
+
18
+ - 01241c2: Backstage version bump to v1.49.2
19
+ moved existing app to app-legacy
20
+ app now runs the NFS model
21
+
22
+ ### Patch Changes
23
+
24
+ - 5d4f070: update how tool call info is retrived
25
+ - afd7a1e: Update translations for Lightspeed.
26
+ - c45aa19: Add stop button to interrupt a streaming conversation
27
+ - c0f9d6a: fixes lightspeed overlay (remove horizontal scrollbar, and adds vertical scrollbar for newly created chats)
28
+ - 8f2c7f2: fix the overlay mode opening underneath the ApplicationDrawer
29
+ - a8b3dd9: Fixed "new chat" cta behavior
30
+ Added vertical scroll when too many models are available
31
+ Removed model grouping/categories in the model selector dropdown
32
+ - 7ce8caa: Added a missing permission screen for the Notebooks tab. When a user lacks the `lightspeed.notebooks.use` permission, the Notebooks tab now displays a "Missing permission" message with a "Go back" button instead of the notebook list.
33
+ - Updated dependencies [024d5a8]
34
+ - Updated dependencies [7db4bed]
35
+ - Updated dependencies [01241c2]
36
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.0.0
37
+
3
38
  ## 1.4.0
4
39
 
5
40
  ### Patch Changes
package/README.md CHANGED
@@ -24,6 +24,9 @@ p, role:default/team_a, lightspeed.chat.create, create, allow
24
24
  p, role:default/team_a, lightspeed.chat.delete, delete, allow
25
25
  p, role:default/team_a, lightspeed.chat.update, update, allow
26
26
 
27
+ # Required for AI Notebooks feature (if enabled)
28
+ p, role:default/team_a, lightspeed.notebooks.use, update, allow
29
+
27
30
  g, user:default/<your-user-name>, role:default/team_a
28
31
 
29
32
  ```
@@ -84,6 +87,62 @@ Lightspeed is a front-end plugin that enables you to interact with any LLM serve
84
87
  1. Open your Backstage application and select a Lightspeed nav item from the **Navigation**.
85
88
  2. Ask you questions to the Lightspeed chatbot.
86
89
 
90
+ ### MCP servers settings
91
+
92
+ Lightspeed includes an MCP servers settings panel where users can:
93
+
94
+ - View configured MCP servers and current status
95
+ - Enable or disable eligible servers
96
+ - Configure a personal token per server
97
+ - See inline status and validation feedback
98
+
99
+ #### Token validation behavior
100
+
101
+ When configuring a server token in the settings modal, the token is validated
102
+ automatically after typing stops briefly. The input shows inline feedback:
103
+
104
+ - Success: `Connection successful`
105
+ - Error: `Authorization failed. Try again.`
106
+
107
+ Users can then save the configuration after validation feedback is displayed.
108
+
109
+ ### AI Notebooks (Developer Preview)
110
+
111
+ AI Notebooks is an experimental feature that enables **document-based conversations with Retrieval-Augmented Generation (RAG)**. This feature allows you to:
112
+
113
+ - **Create persistent notebook sessions** with their own vector databases
114
+ - **Upload and manage documents** to query against, including:
115
+ - Text files (`.txt`, `.md`, `.log`)
116
+ - PDF documents with automatic text extraction
117
+ - Structured data (`.json`, `.yaml`)
118
+ - Web content via URL (up to 20MB per file)
119
+ - **Query your documents** using natural language and get context-aware AI responses
120
+ - **Organize documents** by sessions with metadata and tagging
121
+ - **Maintain context** across conversations - all uploaded documents remain available throughout the session
122
+
123
+ #### How AI Notebooks Works
124
+
125
+ 1. **Session Management**: Create a notebook session, which is backed by its own vector database
126
+ 2. **Document Upload**: Upload documents - they are automatically processed, chunked, and embedded into the vector database
127
+ 3. **RAG Conversations**: Ask questions about your documents - relevant chunks are retrieved and provided as context to the LLM
128
+ 4. **Persistent Storage**: Your documents and conversations remain available throughout the session lifecycle
129
+
130
+ #### Prerequisites for AI Notebooks
131
+
132
+ - AI Notebooks requires a **Llama Stack service** to be running
133
+ - The backend administrator must enable the feature (see [Backend Configuration](../lightspeed-backend/README.md#ai-notebooks-developer-preview))
134
+ - Users need the appropriate RBAC permissions (if enabled)
135
+
136
+ #### Using AI Notebooks
137
+
138
+ 1. Ensure AI Notebooks is enabled in your Backstage instance
139
+ 2. Navigate to the Lightspeed page
140
+ 3. Create a new notebook session or select an existing one
141
+ 4. Upload documents you want to query
142
+ 5. Start asking questions about your uploaded documents
143
+
144
+ For backend configuration and API details, administrators should refer to the [Lightspeed Backend Plugin Documentation](../lightspeed-backend/README.md#ai-notebooks-developer-preview).
145
+
87
146
  ## Loading as Dynamic Plugin
88
147
 
89
148
  #### To configure Lightspeed plugin into Red Hat Developer Hub use this configuration:
@@ -0,0 +1,69 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import CloseIcon from '@mui/icons-material/Close';
3
+ import Fab from '@mui/material/Fab';
4
+ import Tooltip from '@mui/material/Tooltip';
5
+ import { makeStyles } from '@mui/styles';
6
+ import { ChatbotDisplayMode } from '@patternfly/chatbot';
7
+ import { LightspeedFABIcon } from '../components/LightspeedIcon.esm.js';
8
+ import { DOCKED_CONTENT_OFFSET } from '../const.esm.js';
9
+ import { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext.esm.js';
10
+
11
+ const useStyles = makeStyles((theme) => ({
12
+ fab: {
13
+ opacity: "1 !important",
14
+ backgroundColor: `${theme?.palette?.primary?.main ?? "#1976d2"} !important`,
15
+ color: `${theme?.palette?.primary?.contrastText ?? "#fff"} !important`,
16
+ boxShadow: `${theme?.shadows?.[6] ?? "0px 3px 5px -1px rgba(0,0,0,0.2), 0px 6px 10px 0px rgba(0,0,0,0.14), 0px 1px 18px 0px rgba(0,0,0,0.12)"} !important`
17
+ },
18
+ "fab-button": {
19
+ bottom: `calc(${theme?.spacing?.(2) ?? "16px"} + 1.5em)`,
20
+ right: `calc(${theme?.spacing?.(2) ?? "16px"} + 1.5em)`,
21
+ alignItems: "end",
22
+ zIndex: 200,
23
+ display: "flex",
24
+ position: "fixed",
25
+ // When ApplicationDrawer is docked, match main content inset
26
+ "body.docked-drawer-open &": {
27
+ transition: "margin-right 0.3s ease",
28
+ marginRight: DOCKED_CONTENT_OFFSET
29
+ }
30
+ }
31
+ }));
32
+ const LightspeedFAB = () => {
33
+ const { isChatbotActive, toggleChatbot, displayMode } = useLightspeedDrawerContext();
34
+ const fabButton = useStyles();
35
+ if (displayMode === ChatbotDisplayMode.embedded) {
36
+ return null;
37
+ }
38
+ return /* @__PURE__ */ jsx(
39
+ "div",
40
+ {
41
+ className: fabButton["fab-button"],
42
+ id: "lightspeed-fab",
43
+ "data-testid": "lightspeed-fab",
44
+ children: /* @__PURE__ */ jsx(
45
+ Tooltip,
46
+ {
47
+ title: isChatbotActive ? "Close Lightspeed" : "Open Lightspeed",
48
+ placement: "left",
49
+ children: /* @__PURE__ */ jsx(
50
+ Fab,
51
+ {
52
+ color: "primary",
53
+ variant: "circular",
54
+ size: "small",
55
+ onClick: toggleChatbot,
56
+ "aria-label": isChatbotActive ? "lightspeed-close" : "lightspeed-open",
57
+ className: fabButton.fab,
58
+ sx: { borderRadius: "100% !important" },
59
+ children: isChatbotActive ? /* @__PURE__ */ jsx(CloseIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(LightspeedFABIcon, {})
60
+ }
61
+ )
62
+ }
63
+ )
64
+ }
65
+ );
66
+ };
67
+
68
+ export { LightspeedFAB };
69
+ //# sourceMappingURL=LightspeedFAB.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LightspeedFAB.esm.js","sources":["../../src/alpha/LightspeedFAB.tsx"],"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 Close from '@mui/icons-material/Close';\nimport Fab from '@mui/material/Fab';\nimport Tooltip from '@mui/material/Tooltip';\nimport { makeStyles } from '@mui/styles';\nimport { ChatbotDisplayMode } from '@patternfly/chatbot';\n\nimport { LightspeedFABIcon } from '../components/LightspeedIcon';\nimport { DOCKED_CONTENT_OFFSET } from '../const';\nimport { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext';\n\nconst useStyles = makeStyles(theme => ({\n fab: {\n opacity: '1 !important',\n backgroundColor: `${theme?.palette?.primary?.main ?? '#1976d2'} !important`,\n color: `${theme?.palette?.primary?.contrastText ?? '#fff'} !important`,\n boxShadow: `${\n theme?.shadows?.[6] ??\n '0px 3px 5px -1px rgba(0,0,0,0.2), 0px 6px 10px 0px rgba(0,0,0,0.14), 0px 1px 18px 0px rgba(0,0,0,0.12)'\n } !important`,\n },\n 'fab-button': {\n bottom: `calc(${theme?.spacing?.(2) ?? '16px'} + 1.5em)`,\n right: `calc(${theme?.spacing?.(2) ?? '16px'} + 1.5em)`,\n alignItems: 'end',\n zIndex: 200,\n display: 'flex',\n position: 'fixed',\n\n // When ApplicationDrawer is docked, match main content inset\n 'body.docked-drawer-open &': {\n transition: 'margin-right 0.3s ease',\n marginRight: DOCKED_CONTENT_OFFSET,\n },\n },\n}));\n\n/**\n * @alpha\n * Lightspeed Floating action button to open/close the lightspeed chatbot\n */\n\nexport const LightspeedFAB = () => {\n const { isChatbotActive, toggleChatbot, displayMode } =\n useLightspeedDrawerContext();\n const fabButton = useStyles();\n\n if (displayMode === ChatbotDisplayMode.embedded) {\n return null;\n }\n\n return (\n <div\n className={fabButton['fab-button']}\n id=\"lightspeed-fab\"\n data-testid=\"lightspeed-fab\"\n >\n <Tooltip\n title={isChatbotActive ? 'Close Lightspeed' : 'Open Lightspeed'}\n placement=\"left\"\n >\n <Fab\n color=\"primary\"\n variant=\"circular\"\n size=\"small\"\n onClick={toggleChatbot}\n aria-label={isChatbotActive ? 'lightspeed-close' : 'lightspeed-open'}\n className={fabButton.fab}\n sx={{ borderRadius: '100% !important' }}\n >\n {isChatbotActive ? <Close fontSize=\"small\" /> : <LightspeedFABIcon />}\n </Fab>\n </Tooltip>\n </div>\n );\n};\n"],"names":["Close"],"mappings":";;;;;;;;;;AA0BA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,GAAK,EAAA;AAAA,IACH,OAAS,EAAA,cAAA;AAAA,IACT,iBAAiB,CAAG,EAAA,KAAA,EAAO,OAAS,EAAA,OAAA,EAAS,QAAQ,SAAS,CAAA,WAAA,CAAA;AAAA,IAC9D,OAAO,CAAG,EAAA,KAAA,EAAO,OAAS,EAAA,OAAA,EAAS,gBAAgB,MAAM,CAAA,WAAA,CAAA;AAAA,IACzD,WAAW,CACT,EAAA,KAAA,EAAO,OAAU,GAAA,CAAC,KAClB,wGACF,CAAA,WAAA;AAAA,GACF;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,QAAQ,CAAQ,KAAA,EAAA,KAAA,EAAO,OAAU,GAAA,CAAC,KAAK,MAAM,CAAA,SAAA,CAAA;AAAA,IAC7C,OAAO,CAAQ,KAAA,EAAA,KAAA,EAAO,OAAU,GAAA,CAAC,KAAK,MAAM,CAAA,SAAA,CAAA;AAAA,IAC5C,UAAY,EAAA,KAAA;AAAA,IACZ,MAAQ,EAAA,GAAA;AAAA,IACR,OAAS,EAAA,MAAA;AAAA,IACT,QAAU,EAAA,OAAA;AAAA;AAAA,IAGV,2BAA6B,EAAA;AAAA,MAC3B,UAAY,EAAA,wBAAA;AAAA,MACZ,WAAa,EAAA;AAAA;AACf;AAEJ,CAAE,CAAA,CAAA;AAOK,MAAM,gBAAgB,MAAM;AACjC,EAAA,MAAM,EAAE,eAAA,EAAiB,aAAe,EAAA,WAAA,KACtC,0BAA2B,EAAA;AAC7B,EAAA,MAAM,YAAY,SAAU,EAAA;AAE5B,EAAI,IAAA,WAAA,KAAgB,mBAAmB,QAAU,EAAA;AAC/C,IAAO,OAAA,IAAA;AAAA;AAGT,EACE,uBAAA,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,UAAU,YAAY,CAAA;AAAA,MACjC,EAAG,EAAA,gBAAA;AAAA,MACH,aAAY,EAAA,gBAAA;AAAA,MAEZ,QAAA,kBAAA,GAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO,kBAAkB,kBAAqB,GAAA,iBAAA;AAAA,UAC9C,SAAU,EAAA,MAAA;AAAA,UAEV,QAAA,kBAAA,GAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cACC,KAAM,EAAA,SAAA;AAAA,cACN,OAAQ,EAAA,UAAA;AAAA,cACR,IAAK,EAAA,OAAA;AAAA,cACL,OAAS,EAAA,aAAA;AAAA,cACT,YAAA,EAAY,kBAAkB,kBAAqB,GAAA,iBAAA;AAAA,cACnD,WAAW,SAAU,CAAA,GAAA;AAAA,cACrB,EAAA,EAAI,EAAE,YAAA,EAAc,iBAAkB,EAAA;AAAA,cAErC,4CAAmB,GAAA,CAAAA,SAAA,EAAA,EAAM,UAAS,OAAQ,EAAA,CAAA,uBAAM,iBAAkB,EAAA,EAAA;AAAA;AAAA;AACrE;AAAA;AACF;AAAA,GACF;AAEJ;;;;"}
@@ -0,0 +1,6 @@
1
+ import { LightspeedDrawerProvider } from '../components/LightspeedDrawerProvider.esm.js';
2
+
3
+ const LightspeedProvider = LightspeedDrawerProvider;
4
+
5
+ export { LightspeedProvider };
6
+ //# sourceMappingURL=LightspeedProvider.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LightspeedProvider.esm.js","sources":["../../src/alpha/LightspeedProvider.tsx"],"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 { LightspeedDrawerProvider } from '../components/LightspeedDrawerProvider';\n\n/**\n * This file is exported as LightspeedProvider from the alpha entry point.\n * @alpha\n */\n\nexport const LightspeedProvider = LightspeedDrawerProvider;\n"],"names":[],"mappings":";;AAuBO,MAAM,kBAAqB,GAAA;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as react from 'react';
2
+ import * as _red_hat_developer_hub_backstage_plugin_app_react from '@red-hat-developer-hub/backstage-plugin-app-react';
1
3
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
2
4
 
3
5
  /**
@@ -33,6 +35,30 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
33
35
  readonly "prompts.rhdh.message": string;
34
36
  readonly "page.title": string;
35
37
  readonly "page.subtitle": string;
38
+ readonly "tabs.ariaLabel": string;
39
+ readonly "tabs.chat": string;
40
+ readonly "tabs.notebooks": string;
41
+ readonly "tabs.notebooks.empty": string;
42
+ readonly "notebooks.title": string;
43
+ readonly "notebooks.empty.title": string;
44
+ readonly "notebooks.empty.description": string;
45
+ readonly "notebooks.empty.action": string;
46
+ readonly "notebooks.documents": string;
47
+ readonly "notebooks.actions.rename": string;
48
+ readonly "notebooks.actions.delete": string;
49
+ readonly "notebooks.rename.title": string;
50
+ readonly "notebooks.rename.description": string;
51
+ readonly "notebooks.rename.label": string;
52
+ readonly "notebooks.rename.placeholder": string;
53
+ readonly "notebooks.rename.action": string;
54
+ readonly "notebooks.delete.title": string;
55
+ readonly "notebooks.delete.message": string;
56
+ readonly "notebooks.delete.action": string;
57
+ readonly "notebooks.delete.toast": string;
58
+ readonly "notebooks.updated.today": string;
59
+ readonly "notebooks.updated.yesterday": string;
60
+ readonly "notebooks.updated.days": string;
61
+ readonly "notebooks.updated.on": string;
36
62
  readonly "conversation.delete.confirm.title": string;
37
63
  readonly "conversation.delete.confirm.message": string;
38
64
  readonly "conversation.delete.confirm.action": string;
@@ -41,6 +67,9 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
41
67
  readonly "conversation.rename.placeholder": string;
42
68
  readonly "permission.required.title": string;
43
69
  readonly "permission.required.description": string;
70
+ readonly "permission.subject.plugin": string;
71
+ readonly "permission.subject.notebooks": string;
72
+ readonly "permission.notebooks.goBack": string;
44
73
  readonly "footer.accuracy.label": string;
45
74
  readonly "common.cancel": string;
46
75
  readonly "common.close": string;
@@ -80,6 +109,7 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
80
109
  readonly "conversation.addToPinnedChats": string;
81
110
  readonly "conversation.removeFromPinnedChats": string;
82
111
  readonly "conversation.announcement.userMessage": string;
112
+ readonly "conversation.announcement.responseStopped": string;
83
113
  readonly "user.guest": string;
84
114
  readonly "user.loading": string;
85
115
  readonly "tooltip.attach": string;
@@ -127,6 +157,41 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
127
157
  readonly "settings.pinned.disable": string;
128
158
  readonly "settings.pinned.enabled.description": string;
129
159
  readonly "settings.pinned.disabled.description": string;
160
+ readonly "settings.mcp.label": string;
161
+ readonly "mcp.settings.title": string;
162
+ readonly "mcp.settings.selectedCount": string;
163
+ readonly "mcp.settings.closeAriaLabel": string;
164
+ readonly "mcp.settings.readOnlyAccess": string;
165
+ readonly "mcp.settings.tableAriaLabel": string;
166
+ readonly "mcp.settings.enabled": string;
167
+ readonly "mcp.settings.name": string;
168
+ readonly "mcp.settings.status": string;
169
+ readonly "mcp.settings.edit": string;
170
+ readonly "mcp.settings.loading": string;
171
+ readonly "mcp.settings.noneAvailable": string;
172
+ readonly "mcp.settings.status.disabled": string;
173
+ readonly "mcp.settings.status.tokenRequired": string;
174
+ readonly "mcp.settings.status.failed": string;
175
+ readonly "mcp.settings.status.oneTool": string;
176
+ readonly "mcp.settings.status.manyTools": string;
177
+ readonly "mcp.settings.status.unknown": string;
178
+ readonly "mcp.settings.toggleServerAriaLabel": string;
179
+ readonly "mcp.settings.editServerAriaLabel": string;
180
+ readonly "mcp.settings.configureServerTitle": string;
181
+ readonly "mcp.settings.closeConfigureModalAriaLabel": string;
182
+ readonly "mcp.settings.modalDescription": string;
183
+ readonly "mcp.settings.savedToken": string;
184
+ readonly "mcp.settings.personalAccessToken": string;
185
+ readonly "mcp.settings.usingAdminCredential": string;
186
+ readonly "mcp.settings.enterToken": string;
187
+ readonly "mcp.settings.removePersonalToken": string;
188
+ readonly "mcp.settings.token.clearAriaLabel": string;
189
+ readonly "mcp.settings.token.validating": string;
190
+ readonly "mcp.settings.token.savingAndValidating": string;
191
+ readonly "mcp.settings.token.urlUnavailableForValidation": string;
192
+ readonly "mcp.settings.token.invalidCredentials": string;
193
+ readonly "mcp.settings.token.validationFailed": string;
194
+ readonly "mcp.settings.token.connectionSuccessful": string;
130
195
  readonly "toolCall.header": string;
131
196
  readonly "toolCall.thinking": string;
132
197
  readonly "toolCall.executionTime": string;
@@ -157,4 +222,108 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
157
222
  **/
158
223
  declare const lightspeedTranslations: _backstage_frontend_plugin_api.TranslationResource<"plugin.lightspeed">;
159
224
 
160
- export { lightspeedTranslationRef, lightspeedTranslations };
225
+ /**
226
+ * @alpha
227
+ */
228
+ declare const lightspeedFABModule: _backstage_frontend_plugin_api.FrontendModule;
229
+ /**
230
+ * Translation wiring for the language selector (app-config `app.extensions`).
231
+ *
232
+ * @alpha
233
+ */
234
+ declare const lightspeedTranslationsModule: _backstage_frontend_plugin_api.FrontendModule;
235
+ /**
236
+ * Lightspeed plugin for the Backstage new frontend system.
237
+ *
238
+ * @alpha
239
+ */
240
+ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
241
+ root: _backstage_frontend_plugin_api.RouteRef<undefined>;
242
+ lightspeedConversation: _backstage_frontend_plugin_api.SubRouteRef<{
243
+ conversationId: string;
244
+ }>;
245
+ }, {}, {
246
+ "api:lightspeed/lightspeed": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
247
+ kind: "api";
248
+ name: "lightspeed";
249
+ config: {};
250
+ configInput: {};
251
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.AnyApiFactory, "core.api.factory", {}>;
252
+ inputs: {};
253
+ params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
254
+ }>;
255
+ "api:lightspeed/notebooks": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
256
+ kind: "api";
257
+ name: "notebooks";
258
+ config: {};
259
+ configInput: {};
260
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.AnyApiFactory, "core.api.factory", {}>;
261
+ inputs: {};
262
+ params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
263
+ }>;
264
+ "app-drawer-content:lightspeed/lightspeed": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
265
+ kind: "app-drawer-content";
266
+ name: "lightspeed";
267
+ config: {
268
+ defaultWidth: number | undefined;
269
+ resizable: boolean | undefined;
270
+ priority: number | undefined;
271
+ };
272
+ configInput: {
273
+ defaultWidth?: number | undefined;
274
+ resizable?: boolean | undefined;
275
+ priority?: number | undefined;
276
+ };
277
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<_red_hat_developer_hub_backstage_plugin_app_react.AppDrawerContent, "app.drawer.content", {}>;
278
+ inputs: {};
279
+ params: {
280
+ id: string;
281
+ element: React.ReactElement;
282
+ resizable?: boolean;
283
+ defaultWidth?: number;
284
+ priority?: number;
285
+ };
286
+ }>;
287
+ "page:lightspeed": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
288
+ kind: "page";
289
+ name: undefined;
290
+ config: {
291
+ path: string | undefined;
292
+ title: string | undefined;
293
+ };
294
+ configInput: {
295
+ title?: string | undefined;
296
+ path?: string | undefined;
297
+ };
298
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
299
+ optional: true;
300
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
301
+ optional: true;
302
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
303
+ optional: true;
304
+ }>;
305
+ inputs: {
306
+ pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
307
+ optional: true;
308
+ }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
309
+ optional: true;
310
+ }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
311
+ optional: true;
312
+ }>, {
313
+ singleton: false;
314
+ optional: false;
315
+ internal: false;
316
+ }>;
317
+ };
318
+ params: {
319
+ path: string;
320
+ title?: string;
321
+ icon?: _backstage_frontend_plugin_api.IconElement;
322
+ loader?: () => Promise<react.JSX.Element>;
323
+ routeRef?: _backstage_frontend_plugin_api.RouteRef;
324
+ noHeader?: boolean;
325
+ };
326
+ }>;
327
+ }>;
328
+
329
+ export { _default as default, lightspeedFABModule, lightspeedTranslationRef, lightspeedTranslations, lightspeedTranslationsModule };
package/dist/alpha.esm.js CHANGED
@@ -1,3 +1,99 @@
1
- export { lightspeedTranslations } from './translations/index.esm.js';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { createRouteRef, createSubRouteRef, ApiBlueprint, createApiFactory, configApiRef, fetchApiRef, PageBlueprint, createFrontendModule, createFrontendPlugin } from '@backstage/frontend-plugin-api';
3
+ import { AppRootWrapperBlueprint, TranslationBlueprint } from '@backstage/plugin-app-react';
4
+ import { AppDrawerContentBlueprint } from '@red-hat-developer-hub/backstage-plugin-app-react/alpha';
5
+ import { lightspeedApiRef } from './api/api.esm.js';
6
+ import { LightspeedApiClient } from './api/LightspeedApiClient.esm.js';
7
+ import { notebooksApiRef } from './api/notebooksApi.esm.js';
8
+ import { NotebooksApiClient } from './api/NotebooksApiClient.esm.js';
9
+ import { LightspeedChatContainer } from './components/LightspeedChatContainer.esm.js';
10
+ import { LIGHTSPEED_APP_DRAWER_ID } from './const.esm.js';
11
+ import { lightspeedTranslations } from './translations/index.esm.js';
12
+ import { LightspeedFAB } from './alpha/LightspeedFAB.esm.js';
13
+ import { LightspeedProvider } from './alpha/LightspeedProvider.esm.js';
2
14
  export { lightspeedTranslationRef } from './translations/ref.esm.js';
15
+
16
+ const nfsRootRouteRef = createRouteRef();
17
+ const nfsConversationRouteRef = createSubRouteRef({
18
+ parent: nfsRootRouteRef,
19
+ path: "/conversation/:conversationId"
20
+ });
21
+ const lightspeedApi = ApiBlueprint.make({
22
+ name: "lightspeed",
23
+ params: (defineParams) => defineParams(
24
+ createApiFactory({
25
+ api: lightspeedApiRef,
26
+ deps: {
27
+ configApi: configApiRef,
28
+ fetchApi: fetchApiRef
29
+ },
30
+ factory: ({ configApi, fetchApi }) => new LightspeedApiClient({ configApi, fetchApi })
31
+ })
32
+ )
33
+ });
34
+ const notebooksApi = ApiBlueprint.make({
35
+ name: "notebooks",
36
+ params: (defineParams) => defineParams(
37
+ createApiFactory({
38
+ api: notebooksApiRef,
39
+ deps: {
40
+ configApi: configApiRef,
41
+ fetchApi: fetchApiRef
42
+ },
43
+ factory: ({ configApi, fetchApi }) => new NotebooksApiClient({ configApi, fetchApi })
44
+ })
45
+ )
46
+ });
47
+ const lightspeedPage = PageBlueprint.make({
48
+ params: {
49
+ path: "/lightspeed",
50
+ routeRef: nfsRootRouteRef,
51
+ noHeader: true,
52
+ loader: () => import('./components/Router.esm.js').then((m) => /* @__PURE__ */ jsx(m.Router, {}))
53
+ }
54
+ });
55
+ const lightspeedDrawer = AppDrawerContentBlueprint.make({
56
+ name: "lightspeed",
57
+ params: {
58
+ id: LIGHTSPEED_APP_DRAWER_ID,
59
+ element: /* @__PURE__ */ jsx(LightspeedChatContainer, {}),
60
+ resizable: false,
61
+ defaultWidth: 400,
62
+ priority: 100
63
+ }
64
+ });
65
+ const lightspeedFAB = AppRootWrapperBlueprint.make({
66
+ name: "lightspeed-fab",
67
+ params: {
68
+ component: ({ children }) => /* @__PURE__ */ jsxs(LightspeedProvider, { children: [
69
+ /* @__PURE__ */ jsx(LightspeedFAB, {}),
70
+ children
71
+ ] })
72
+ }
73
+ });
74
+ const lightspeedFABModule = createFrontendModule({
75
+ pluginId: "app",
76
+ extensions: [lightspeedFAB]
77
+ });
78
+ const lightspeedTranslationsModule = createFrontendModule({
79
+ pluginId: "app",
80
+ extensions: [
81
+ TranslationBlueprint.make({
82
+ name: "lightspeed-translations",
83
+ params: {
84
+ resource: lightspeedTranslations
85
+ }
86
+ })
87
+ ]
88
+ });
89
+ var index = createFrontendPlugin({
90
+ pluginId: "lightspeed",
91
+ extensions: [lightspeedApi, notebooksApi, lightspeedPage, lightspeedDrawer],
92
+ routes: {
93
+ root: nfsRootRouteRef,
94
+ lightspeedConversation: nfsConversationRouteRef
95
+ }
96
+ });
97
+
98
+ export { index as default, lightspeedFABModule, lightspeedTranslations, lightspeedTranslationsModule };
3
99
  //# sourceMappingURL=alpha.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"alpha.esm.js","sources":["../src/alpha/index.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiBlueprint,\n configApiRef,\n createApiFactory,\n createFrontendModule,\n createFrontendPlugin,\n createRouteRef,\n createSubRouteRef,\n fetchApiRef,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport {\n AppRootWrapperBlueprint,\n TranslationBlueprint,\n} from '@backstage/plugin-app-react';\n\nimport { AppDrawerContentBlueprint } from '@red-hat-developer-hub/backstage-plugin-app-react/alpha';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { LightspeedApiClient } from '../api/LightspeedApiClient';\nimport { notebooksApiRef } from '../api/notebooksApi';\nimport { NotebooksApiClient } from '../api/NotebooksApiClient';\nimport { LightspeedChatContainer } from '../components/LightspeedChatContainer';\nimport { LIGHTSPEED_APP_DRAWER_ID } from '../const';\nimport { lightspeedTranslations } from '../translations';\nimport { LightspeedFAB } from './LightspeedFAB';\nimport { LightspeedProvider } from './LightspeedProvider';\n\nconst nfsRootRouteRef = createRouteRef();\nconst nfsConversationRouteRef = createSubRouteRef({\n parent: nfsRootRouteRef,\n path: '/conversation/:conversationId',\n});\n\nconst lightspeedApi = ApiBlueprint.make({\n name: 'lightspeed',\n params: defineParams =>\n defineParams(\n createApiFactory({\n api: lightspeedApiRef,\n deps: {\n configApi: configApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, fetchApi }) =>\n new LightspeedApiClient({ configApi, fetchApi }),\n }),\n ),\n});\n\nconst notebooksApi = ApiBlueprint.make({\n name: 'notebooks',\n params: defineParams =>\n defineParams(\n createApiFactory({\n api: notebooksApiRef,\n deps: {\n configApi: configApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ configApi, fetchApi }) =>\n new NotebooksApiClient({ configApi, fetchApi }),\n }),\n ),\n});\n\nconst lightspeedPage = PageBlueprint.make({\n params: {\n path: '/lightspeed',\n routeRef: nfsRootRouteRef,\n noHeader: true,\n loader: () => import('../components/Router').then(m => <m.Router />),\n },\n});\n\nconst lightspeedDrawer = AppDrawerContentBlueprint.make({\n name: 'lightspeed',\n params: {\n id: LIGHTSPEED_APP_DRAWER_ID,\n element: <LightspeedChatContainer />,\n resizable: false,\n defaultWidth: 400,\n priority: 100,\n },\n});\n\n/**\n * Lightspeed FAB module\n * @alpha\n */\nconst lightspeedFAB = AppRootWrapperBlueprint.make({\n name: 'lightspeed-fab',\n params: {\n component: ({ children }) => (\n <LightspeedProvider>\n <LightspeedFAB />\n {children}\n </LightspeedProvider>\n ),\n },\n});\n\n/**\n * @alpha\n */\nexport const lightspeedFABModule = createFrontendModule({\n pluginId: 'app',\n extensions: [lightspeedFAB],\n});\n\n/**\n * Translation wiring for the language selector (app-config `app.extensions`).\n *\n * @alpha\n */\nexport const lightspeedTranslationsModule = createFrontendModule({\n pluginId: 'app',\n extensions: [\n TranslationBlueprint.make({\n name: 'lightspeed-translations',\n params: {\n resource: lightspeedTranslations,\n },\n }),\n ],\n});\n\n/**\n * Lightspeed plugin for the Backstage new frontend system.\n *\n * @alpha\n */\nexport default createFrontendPlugin({\n pluginId: 'lightspeed',\n extensions: [lightspeedApi, notebooksApi, lightspeedPage, lightspeedDrawer],\n routes: {\n root: nfsRootRouteRef,\n lightspeedConversation: nfsConversationRouteRef,\n },\n});\n\nexport * from '../translations';\n"],"names":[],"mappings":"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"}
@@ -115,6 +115,25 @@ class LightspeedApiClient {
115
115
  const response = await result.json();
116
116
  return response.conversations ?? [];
117
117
  }
118
+ async stopMessage(requestId) {
119
+ const baseUrl = await this.getBaseUrl();
120
+ const response = await this.fetchApi.fetch(
121
+ `${baseUrl}/v1/query/interrupt`,
122
+ {
123
+ method: "POST",
124
+ headers: {
125
+ "Content-Type": "application/json"
126
+ },
127
+ body: JSON.stringify({ request_id: requestId })
128
+ }
129
+ );
130
+ if (!response.ok) {
131
+ throw new Error(
132
+ `failed to stop message, status ${response.status}: ${response.statusText}`
133
+ );
134
+ }
135
+ return await response.json();
136
+ }
118
137
  async deleteConversation(conversation_id) {
119
138
  const baseUrl = await this.getBaseUrl();
120
139
  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 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
+ {"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":"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"}