@red-hat-developer-hub/backstage-plugin-lightspeed 0.5.5 → 0.5.6
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 +11 -1
- package/dist/api/LightspeedApiClient.esm.js +34 -0
- package/dist/api/LightspeedApiClient.esm.js.map +1 -1
- package/dist/api/api.esm.js.map +1 -1
- package/dist/components/LightSpeedChat.esm.js +3 -1
- package/dist/components/LightSpeedChat.esm.js.map +1 -1
- package/dist/components/LightspeedChatBox.esm.js +10 -2
- package/dist/components/LightspeedChatBox.esm.js.map +1 -1
- package/dist/const.esm.js +2 -2
- package/dist/const.esm.js.map +1 -1
- package/dist/hooks/useCaptureFeedback.esm.js +21 -0
- package/dist/hooks/useCaptureFeedback.esm.js.map +1 -0
- package/dist/hooks/useConversationMessages.esm.js +5 -6
- package/dist/hooks/useConversationMessages.esm.js.map +1 -1
- package/dist/hooks/useFeedbackActions.esm.js +246 -0
- package/dist/hooks/useFeedbackActions.esm.js.map +1 -0
- package/dist/hooks/useFeedbackState.esm.js +93 -0
- package/dist/hooks/useFeedbackState.esm.js.map +1 -0
- package/dist/hooks/useFeedbackStatus.esm.js +14 -0
- package/dist/hooks/useFeedbackStatus.esm.js.map +1 -0
- package/dist/types.esm.js.map +1 -1
- package/dist/utils/lightspeed-chatbox-utils.esm.js +6 -7
- package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
## @red-hat-developer-hub/backstage-plugin-lightspeed
|
|
2
2
|
|
|
3
|
+
## 0.5.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2ba3742: Add feedback API endpoints and controls in the UI to collect user feedback
|
|
8
|
+
- bab4647: Update functional and legal disclaimers
|
|
9
|
+
- 9133ae8: Add description in message source card
|
|
10
|
+
- a79f849: Updated dependency `prettier` to `3.6.2`.
|
|
11
|
+
- Updated dependencies [a79f849]
|
|
12
|
+
- @red-hat-developer-hub/backstage-plugin-lightspeed-common@0.3.3
|
|
13
|
+
|
|
3
14
|
## 0.5.5
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
|
6
17
|
|
|
7
18
|
- 5d80736: Renamed permissions to align with the updated naming convention:
|
|
8
|
-
|
|
9
19
|
- `lightspeed.conversations.read` → `lightspeed.chat.read`
|
|
10
20
|
- `lightspeed.conversations.create` → `lightspeed.chat.create`
|
|
11
21
|
- `lightspeed.conversations.delete` → `lightspeed.chat.delete`
|
|
@@ -116,6 +116,40 @@ class LightspeedApiClient {
|
|
|
116
116
|
}
|
|
117
117
|
return { success: true };
|
|
118
118
|
}
|
|
119
|
+
getFeedbackStatus = async () => {
|
|
120
|
+
const baseUrl = await this.getBaseUrl();
|
|
121
|
+
const response = await this.fetchApi.fetch(
|
|
122
|
+
`${baseUrl}/v1/feedback/status`,
|
|
123
|
+
{
|
|
124
|
+
method: "GET",
|
|
125
|
+
headers: {}
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`failed to GET feedback status, status ${response.status}: ${response.statusText}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const result = await response.json();
|
|
134
|
+
return result?.status?.enabled ?? false;
|
|
135
|
+
};
|
|
136
|
+
captureFeedback = async (payload) => {
|
|
137
|
+
const baseUrl = await this.getBaseUrl();
|
|
138
|
+
const response = await this.fetchApi.fetch(`${baseUrl}/v1/feedback`, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
accept: "application/json"
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify(payload)
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`failed to capture feedback, status ${response.status}: ${response.statusText}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return await response.json();
|
|
152
|
+
};
|
|
119
153
|
}
|
|
120
154
|
|
|
121
155
|
export { LightspeedApiClient };
|
|
@@ -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 { TEMP_CONVERSATION_ID } from '../const';\nimport { Attachment } from '../types';\nimport { LightspeedAPI } from './api';\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\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 getServerUrl() {\n // Currently supports a single llm server\n return `${this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('url')}`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: 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: this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('id'), // Currently supports a single llm server\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 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 return response?.data ? response.data : [];\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}/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}/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}/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to delete conversation, status ${response.status}: ${response.statusText}`,\n );\n }\n return { success: true };\n }\n}\n"],"names":[],"mappings":";;AA2BO,MAAM,mBAA6C,CAAA;AAAA,EACvC,SAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAkB,EAAA;AAC5B,IAAA,IAAA,CAAK,YAAY,OAAQ,CAAA,SAAA;AACzB,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA;AAAA;AAC1B,EAEA,MAAM,UAAa,GAAA;AACjB,IAAA,OAAO,CAAG,EAAA,IAAA,CAAK,SAAU,CAAA,SAAA,CAAU,iBAAiB,CAAC,CAAA,eAAA,CAAA;AAAA;AACvD,EAEA,YAAe,GAAA;AAEb,IAAO,OAAA,CAAA,EAAG,IAAK,CAAA,SAAA,CACZ,cAAe,CAAA,oBAAoB,EAAE,CAAC,CAAA,CACtC,iBAAkB,CAAA,KAAK,CAAC,CAAA,CAAA;AAAA;AAC7B,EAEA,MAAM,aAAA,CACJ,MACA,EAAA,aAAA,EACA,iBACA,WACA,EAAA;AACA,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AAEtC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,SAAS,KAAM,CAAA,CAAA,EAAG,OAAO,CAAa,SAAA,CAAA,EAAA;AAAA,MAChE,MAAQ,EAAA,MAAA;AAAA,MACR,OAAS,EAAA;AAAA,QACP,cAAgB,EAAA;AAAA,OAClB;AAAA,MACA,IAAA,EAAM,KAAK,SAAU,CAAA;AAAA,QACnB,eAAA,EACE,eAAoB,KAAA,oBAAA,GAChB,SACA,GAAA,eAAA;AAAA,QACN,KAAO,EAAA,aAAA;AAAA,QACP,QAAA,EAAU,KAAK,SACZ,CAAA,cAAA,CAAe,oBAAoB,CAAE,CAAA,CAAC,CACtC,CAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA;AAAA,QACzB,KAAO,EAAA,MAAA;AAAA,QACP;AAAA,OACD;AAAA,KACF,CAAA;AAED,IAAI,IAAA,CAAC,SAAS,IAAM,EAAA;AAClB,MAAM,MAAA,IAAI,MAAM,uDAAuD,CAAA;AAAA;AAGzE,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAA,MAAM,IAAO,GAAA,MAAM,QAAS,CAAA,IAAA,CAAK,SAAU,EAAA;AAC3C,MAAM,MAAA,MAAA,GAAS,KAAK,IAAK,EAAA;AACzB,MAAM,MAAA,OAAA,GAAU,IAAI,WAAA,CAAY,OAAO,CAAA;AACvC,MAAM,MAAA,IAAA,GAAO,MAAM,MAAO,CAAA,IAAA,CAAK,CAAC,EAAE,IAAA,EAAM,OAAY,KAAA;AAClD,QAAA,IAAI,IAAM,EAAA;AACR,UAAO,OAAA,EAAA;AAAA;AAET,QAAO,OAAA,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,OAC5B,CAAA;AACD,MAAM,MAAA,YAAA,GAAe,IAAK,CAAA,KAAA,CAAM,IAAI,CAAA;AACpC,MAAA,IAAI,cAAc,KAAO,EAAA;AACvB,QAAA,MAAM,IAAI,KAAA,CAAM,CAA6B,0BAAA,EAAA,YAAA,CAAa,KAAK,CAAE,CAAA,CAAA;AAAA;AACnE;AAEF,IAAO,OAAA,QAAA,CAAS,KAAK,SAAU,EAAA;AAAA;AACjC,EAEA,MAAc,QAAQ,GAAa,EAAA;AACjC,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,QAAA,CAAS,MAAM,GAAK,EAAA;AAAA,MAC9C,OAAS,EAAA;AAAA,QACP,cAAgB,EAAA;AAAA;AAClB,KACD,CAAA;AACD,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAgC,6BAAA,EAAA,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OACzE;AAAA;AAEF,IAAO,OAAA,QAAA;AAAA;AACT,EAEA,MAAM,YAAe,GAAA;AACnB,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAQ,CAAA,CAAA,EAAG,OAAO,CAAY,UAAA,CAAA,CAAA;AAExD,IAAI,IAAA,CAAC,OAAO,EAAI,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAgC,6BAAA,EAAA,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OACrE;AAAA;AAGF,IAAM,MAAA,QAAA,GAAW,MAAM,MAAA,CAAO,IAAK,EAAA;AACnC,IAAA,OAAO,QAAU,EAAA,IAAA,GAAO,QAAS,CAAA,IAAA,GAAO,EAAC;AAAA;AAC3C,EAEA,MAAM,wBAAwB,eAAyB,EAAA;AACrD,IAAA,IAAI,oBAAoB,oBAAsB,EAAA;AAC5C,MAAA,OAAO,EAAC;AAAA;AAEV,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAM,MAAA,MAAA,GAAS,MAAM,IAAK,CAAA,OAAA;AAAA,MACxB,CAAG,EAAA,OAAO,CAAkB,eAAA,EAAA,kBAAA,CAAmB,eAAe,CAAC,CAAA;AAAA,KACjE;AACA,IAAI,IAAA,CAAC,OAAO,EAAI,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAA+C,4CAAA,EAAA,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OACpF;AAAA;AAEF,IAAM,MAAA,QAAA,GAAW,MAAM,MAAA,CAAO,IAAK,EAAA;AACnC,IAAO,OAAA,QAAA,CAAS,gBAAgB,EAAC;AAAA;AACnC,EAEA,MAAM,gBAAmB,GAAA;AACvB,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,OAAQ,CAAA,CAAA,EAAG,OAAO,CAAgB,cAAA,CAAA,CAAA;AAE5D,IAAI,IAAA,CAAC,OAAO,EAAI,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAsC,mCAAA,EAAA,MAAA,CAAO,MAAM,CAAA,EAAA,EAAK,OAAO,UAAU,CAAA;AAAA,OAC3E;AAAA;AAGF,IAAM,MAAA,QAAA,GAAW,MAAM,MAAA,CAAO,IAAK,EAAA;AACnC,IAAO,OAAA,QAAA,CAAS,iBAAiB,EAAC;AAAA;AACpC,EAEA,MAAM,mBAAmB,eAAyB,EAAA;AAChD,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AAEtC,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAG,EAAA,OAAO,CAAkB,eAAA,EAAA,kBAAA,CAAmB,eAAe,CAAC,CAAA,CAAA;AAAA,MAC/D;AAAA,QACE,MAAQ,EAAA,QAAA;AAAA,QACR,SAAS;AAAC;AACZ,KACF;AAEA,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAyC,sCAAA,EAAA,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA;AAAA,OAClF;AAAA;AAEF,IAAO,OAAA,EAAE,SAAS,IAAK,EAAA;AAAA;AAE3B;;;;"}
|
|
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 { TEMP_CONVERSATION_ID } from '../const';\nimport { Attachment, CaptureFeedback } from '../types';\nimport { LightspeedAPI } from './api';\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\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 getServerUrl() {\n // Currently supports a single llm server\n return `${this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('url')}`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: 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: this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('id'), // Currently supports a single llm server\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 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 return response?.data ? response.data : [];\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}/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}/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}/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to delete conversation, status ${response.status}: ${response.statusText}`,\n );\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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"}
|
package/dist/api/api.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.esm.js","sources":["../../src/api/api.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nimport OpenAI from 'openai';\n\nimport {
|
|
1
|
+
{"version":3,"file":"api.esm.js","sources":["../../src/api/api.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nimport OpenAI from 'openai';\n\nimport {\n Attachment,\n BaseMessage,\n CaptureFeedback,\n ConversationList,\n} from '../types';\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<OpenAI.Models.Model[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n getFeedbackStatus: () => Promise<boolean>;\n captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>;\n};\n\nexport const lightspeedApiRef = createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n});\n"],"names":[],"mappings":";;AA4CO,MAAM,mBAAmB,YAA4B,CAAA;AAAA,EAC1D,EAAI,EAAA;AACN,CAAC;;;;"}
|
|
@@ -359,7 +359,9 @@ const LightspeedChat = ({
|
|
|
359
359
|
profileLoading,
|
|
360
360
|
announcement,
|
|
361
361
|
ref: scrollToBottomRef,
|
|
362
|
-
welcomePrompts
|
|
362
|
+
welcomePrompts,
|
|
363
|
+
conversationId,
|
|
364
|
+
isStreaming: isSendButtonDisabled
|
|
363
365
|
}
|
|
364
366
|
)),
|
|
365
367
|
/* @__PURE__ */ React__default.createElement(ChatbotFooter, { className: classes.footer }, /* @__PURE__ */ React__default.createElement(FilePreview, null), /* @__PURE__ */ React__default.createElement(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LightSpeedChat.esm.js","sources":["../../src/components/LightSpeedChat.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 React from 'react';\nimport { FileRejection } from 'react-dropzone/.';\n\nimport { ErrorPanel } from '@backstage/core-components';\n\nimport { Box, makeStyles } from '@material-ui/core';\nimport {\n Chatbot,\n ChatbotAlert,\n ChatbotContent,\n ChatbotDisplayMode,\n ChatbotFooter,\n ChatbotFootnote,\n ChatbotHeader,\n ChatbotHeaderMain,\n ChatbotHeaderMenu,\n ChatbotHeaderTitle,\n FileDropZone,\n MessageBar,\n MessageProps,\n} from '@patternfly/chatbot';\nimport ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';\nimport { DropdownItem, DropEvent, Title } from '@patternfly/react-core';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { supportedFileTypes, TEMP_CONVERSATION_ID } from '../const';\nimport {\n useBackstageUserIdentity,\n useConversationMessages,\n useConversations,\n useDeleteConversation,\n useIsMobile,\n useLastOpenedConversation,\n useLightspeedDeletePermission,\n} from '../hooks';\nimport { useWelcomePrompts } from '../hooks/useWelcomePrompts';\nimport { ConversationSummary } from '../types';\nimport { getAttachments } from '../utils/attachment-utils';\nimport {\n getCategorizeMessages,\n getFootnoteProps,\n} from '../utils/lightspeed-chatbox-utils';\nimport Attachment from './Attachment';\nimport { useFileAttachmentContext } from './AttachmentContext';\nimport { DeleteModal } from './DeleteModal';\nimport FilePreview from './FilePreview';\nimport { LightspeedChatBox } from './LightspeedChatBox';\nimport { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader';\n\nconst useStyles = makeStyles(theme => ({\n body: {\n // remove default margin and padding from common elements\n '& h1, & h2, & h3, & h4, & h5, & h6, & p, & ul, & ol, & li': {\n margin: 0,\n padding: 0,\n },\n },\n header: {\n padding: `${theme.spacing(3)}px !important`,\n },\n errorContainer: {\n padding: theme.spacing(3),\n },\n headerMenu: {\n // align hamburger icon with title\n '& .pf-v6-c-button': {\n display: 'flex',\n alignItems: 'center',\n },\n },\n headerTitle: {\n justifyContent: 'left !important',\n },\n footer: {\n '&>.pf-chatbot__footer-container': {\n width: '95% !important',\n maxWidth: 'unset !important',\n },\n },\n footerPopover: {\n '& img': {\n maxWidth: '100%',\n },\n },\n}));\n\ntype LightspeedChatProps = {\n selectedModel: string;\n userName?: string;\n avatar?: string;\n profileLoading: boolean;\n handleSelectedModel: (item: string) => void;\n models: { label: string; value: string }[];\n};\n\nexport const LightspeedChat = ({\n selectedModel,\n userName,\n avatar,\n profileLoading,\n handleSelectedModel,\n models,\n}: LightspeedChatProps) => {\n const isMobile = useIsMobile();\n const classes = useStyles();\n const user = useBackstageUserIdentity();\n const [filterValue, setFilterValue] = React.useState<string>('');\n const [announcement, setAnnouncement] = React.useState<string>('');\n const [conversationId, setConversationId] = React.useState<string>('');\n const [isDrawerOpen, setIsDrawerOpen] = React.useState<boolean>(!isMobile);\n const [newChatCreated, setNewChatCreated] = React.useState<boolean>(false);\n const [isSendButtonDisabled, setIsSendButtonDisabled] =\n React.useState<boolean>(false);\n const [error, setError] = React.useState<Error | null>(null);\n const [targetConversationId, setTargetConversationId] =\n React.useState<string>('');\n const [isDeleteModalOpen, setIsDeleteModalOpen] =\n React.useState<boolean>(false);\n const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } =\n useLastOpenedConversation(user);\n\n const {\n uploadError,\n showAlert,\n fileContents,\n setShowAlert,\n setFileContents,\n handleFileUpload,\n setUploadError,\n } = useFileAttachmentContext();\n\n // Sync conversationId with lastOpenedId whenever lastOpenedId changes\n React.useEffect(() => {\n if (isReady && lastOpenedId !== null) {\n setConversationId(lastOpenedId);\n }\n }, [lastOpenedId, isReady]);\n\n const queryClient = useQueryClient();\n\n const {\n data: conversations = [],\n isLoading,\n isRefetching,\n } = useConversations();\n const { mutateAsync: deleteConversation } = useDeleteConversation();\n const { allowed: hasDeleteAccess } = useLightspeedDeletePermission();\n const samplePrompts = useWelcomePrompts();\n React.useEffect(() => {\n if (user && lastOpenedId === null && isReady) {\n setConversationId(TEMP_CONVERSATION_ID);\n setNewChatCreated(true);\n }\n }, [user, isReady, lastOpenedId, setConversationId]);\n\n React.useEffect(() => {\n // Clear last opened conversationId when there are no conversations.\n if (\n !isLoading &&\n !isRefetching &&\n conversations.length === 0 &&\n lastOpenedId\n ) {\n clearLastOpenedId();\n }\n }, [isLoading, isRefetching, conversations, lastOpenedId, clearLastOpenedId]);\n\n React.useEffect(() => {\n // Update last opened conversation whenever `conversationId` changes\n if (conversationId) {\n setLastOpenedId(conversationId);\n }\n }, [conversationId, setLastOpenedId]);\n\n const onStart = (conv_id: string) => {\n setConversationId(conv_id);\n };\n\n const onComplete = (message: string) => {\n setIsSendButtonDisabled(false);\n setAnnouncement(`Message from Bot: ${message}`);\n queryClient.invalidateQueries({\n queryKey: ['conversations'],\n });\n queryClient.invalidateQueries({\n queryKey: ['conversationMessages', conversationId],\n });\n setNewChatCreated(false);\n };\n\n const { conversationMessages, handleInputPrompt, scrollToBottomRef } =\n useConversationMessages(\n conversationId,\n userName,\n selectedModel,\n avatar,\n onComplete,\n onStart,\n );\n\n const [messages, setMessages] =\n React.useState<MessageProps[]>(conversationMessages);\n\n const sendMessage = (message: string | number) => {\n if (conversationId !== TEMP_CONVERSATION_ID) {\n setNewChatCreated(false);\n }\n setAnnouncement(\n `Message from User: ${prompt}. Message from Bot is loading.`,\n );\n handleInputPrompt(message.toString(), getAttachments(fileContents));\n setIsSendButtonDisabled(true);\n setFileContents([]);\n };\n\n const onNewChat = React.useCallback(() => {\n (async () => {\n if (conversationId !== TEMP_CONVERSATION_ID) {\n setMessages([]);\n setFileContents([]);\n setUploadError({ message: null });\n setConversationId(TEMP_CONVERSATION_ID);\n setNewChatCreated(true);\n }\n })();\n }, [\n conversationId,\n setConversationId,\n setMessages,\n setUploadError,\n setFileContents,\n ]);\n\n const openDeleteModal = (conversation_id: string) => {\n setTargetConversationId(conversation_id);\n setIsDeleteModalOpen(true);\n };\n\n const handleDeleteConversation = React.useCallback(() => {\n (async () => {\n try {\n await deleteConversation({\n conversation_id: targetConversationId,\n invalidateCache: false,\n });\n if (targetConversationId === lastOpenedId) {\n onNewChat();\n clearLastOpenedId();\n }\n setIsDeleteModalOpen(false);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n setError(e);\n }\n })();\n }, [\n deleteConversation,\n clearLastOpenedId,\n lastOpenedId,\n onNewChat,\n targetConversationId,\n ]);\n\n const additionalMessageProps = React.useCallback(\n (conversationSummary: ConversationSummary) => ({\n menuItems: (\n <DropdownItem\n isDisabled={!hasDeleteAccess}\n onClick={() => openDeleteModal(conversationSummary.conversation_id)}\n >\n Delete\n </DropdownItem>\n ),\n }),\n [hasDeleteAccess],\n );\n const categorizedMessages = getCategorizeMessages(\n conversations,\n additionalMessageProps,\n );\n\n const filterConversations = React.useCallback(\n (targetValue: string) => {\n const filteredConversations = Object.entries(categorizedMessages).reduce(\n (acc, [key, items]) => {\n const filteredItems = items.filter(item =>\n item.text\n .toLocaleLowerCase('en-US')\n .includes(targetValue.toLocaleLowerCase('en-US')),\n );\n if (filteredItems.length > 0) {\n acc[key] = filteredItems;\n }\n return acc;\n },\n {} as any,\n );\n return filteredConversations;\n },\n [categorizedMessages],\n );\n\n React.useEffect(() => {\n setMessages(conversationMessages);\n }, [conversationMessages]);\n\n const onSelectActiveItem = React.useCallback(\n (\n _: React.MouseEvent | undefined,\n selectedItem: string | number | undefined,\n ) => {\n setNewChatCreated(false);\n setConversationId((c_id: string) => {\n if (c_id !== selectedItem) {\n return String(selectedItem);\n }\n return c_id;\n });\n setFileContents([]);\n setUploadError({ message: null });\n scrollToBottomRef.current?.scrollToBottom();\n },\n [setConversationId, setUploadError, setFileContents, scrollToBottomRef],\n );\n\n const conversationFound = !!conversations.find(\n (c: ConversationSummary) => c.conversation_id === conversationId,\n );\n\n const welcomePrompts =\n (newChatCreated && conversationMessages.length === 0) ||\n (!conversationFound && conversationMessages.length === 0)\n ? samplePrompts?.map(prompt => ({\n title: prompt.title,\n message: prompt.message,\n onClick: () => {\n sendMessage(prompt.message);\n },\n }))\n : [];\n\n const handleFilter = React.useCallback((value: string) => {\n setFilterValue(value);\n }, []);\n\n const onDrawerToggle = React.useCallback(() => {\n setIsDrawerOpen(isOpen => !isOpen);\n }, []);\n\n const handleAttach = (data: File[], event: DropEvent) => {\n event.preventDefault();\n handleFileUpload(data);\n };\n\n const onAttachRejected = (data: FileRejection[]) => {\n data.forEach(attachment => {\n if (!!attachment.errors.find(e => e.code === 'file-invalid-type')) {\n setShowAlert(true);\n setUploadError({\n message:\n 'Unsupported file type. Supported types are: .txt, .yaml, .json and .xml.',\n });\n }\n });\n };\n\n if (error) {\n return (\n <Box padding={1}>\n <ErrorPanel error={error} />\n </Box>\n );\n }\n\n return (\n <>\n {isDeleteModalOpen && (\n <DeleteModal\n isOpen={isDeleteModalOpen}\n onClose={() => setIsDeleteModalOpen(false)}\n onConfirm={handleDeleteConversation}\n />\n )}\n <Chatbot\n displayMode={ChatbotDisplayMode.embedded}\n className={classes.body}\n >\n <ChatbotHeader className={classes.header}>\n <ChatbotHeaderMain>\n <ChatbotHeaderMenu\n aria-expanded={isDrawerOpen}\n onMenuToggle={() => setIsDrawerOpen(!isDrawerOpen)}\n className={classes.headerMenu}\n />\n <ChatbotHeaderTitle className={classes.headerTitle}>\n <Title headingLevel=\"h1\" size=\"3xl\">\n Developer Lightspeed\n </Title>\n </ChatbotHeaderTitle>\n </ChatbotHeaderMain>\n\n <LightspeedChatBoxHeader\n selectedModel={selectedModel}\n handleSelectedModel={item => handleSelectedModel(item)}\n models={models}\n />\n </ChatbotHeader>\n <ChatbotConversationHistoryNav\n drawerPanelContentProps={{ isResizable: true, minSize: '200px' }}\n reverseButtonOrder\n displayMode={ChatbotDisplayMode.embedded}\n onDrawerToggle={onDrawerToggle}\n isDrawerOpen={isDrawerOpen}\n setIsDrawerOpen={setIsDrawerOpen}\n activeItemId={conversationId}\n onSelectActiveItem={onSelectActiveItem}\n conversations={filterConversations(filterValue)}\n onNewChat={newChatCreated ? undefined : onNewChat}\n handleTextInputChange={handleFilter}\n searchInputPlaceholder=\"Search previous chats...\"\n drawerContent={\n <FileDropZone\n onFileDrop={(e, data) => handleAttach(data, e)}\n displayMode={ChatbotDisplayMode.embedded}\n infoText=\"Supported file types are: .txt, .yaml, .json and .xml. The maximum file size is 25 MB.\"\n allowedFileTypes={supportedFileTypes}\n onAttachRejected={onAttachRejected}\n >\n {showAlert && uploadError.message && (\n <div className={classes.errorContainer}>\n <ChatbotAlert\n component=\"h4\"\n title=\"File upload failed\"\n variant={uploadError.type ?? 'danger'}\n isInline\n onClose={() => setUploadError({ message: null })}\n >\n {uploadError.message}\n </ChatbotAlert>\n </div>\n )}\n\n <ChatbotContent>\n <LightspeedChatBox\n userName={userName}\n messages={messages}\n profileLoading={profileLoading}\n announcement={announcement}\n ref={scrollToBottomRef}\n welcomePrompts={welcomePrompts}\n />\n </ChatbotContent>\n <ChatbotFooter className={classes.footer}>\n <FilePreview />\n <MessageBar\n onSendMessage={sendMessage}\n isSendButtonDisabled={isSendButtonDisabled}\n hasAttachButton\n handleAttach={handleAttach}\n hasMicrophoneButton\n buttonProps={{\n attach: {\n inputTestId: 'attachment-input',\n },\n }}\n allowedFileTypes={supportedFileTypes}\n onAttachRejected={onAttachRejected}\n placeholder=\"Send a message and optionally upload a JSON, YAML, TXT, or XML file...\"\n />\n <ChatbotFootnote {...getFootnoteProps(classes.footerPopover)} />\n </ChatbotFooter>\n </FileDropZone>\n }\n />\n </Chatbot>\n <Attachment />\n </>\n );\n};\n"],"names":["React","prompt"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,IAAM,EAAA;AAAA;AAAA,IAEJ,2DAA6D,EAAA;AAAA,MAC3D,MAAQ,EAAA,CAAA;AAAA,MACR,OAAS,EAAA;AAAA;AACX,GACF;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,OAAS,EAAA,CAAA,EAAG,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAC,CAAA,aAAA;AAAA,GAC9B;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,OAAA,EAAS,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA,GAC1B;AAAA,EACA,UAAY,EAAA;AAAA;AAAA,IAEV,mBAAqB,EAAA;AAAA,MACnB,OAAS,EAAA,MAAA;AAAA,MACT,UAAY,EAAA;AAAA;AACd,GACF;AAAA,EACA,WAAa,EAAA;AAAA,IACX,cAAgB,EAAA;AAAA,GAClB;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,iCAAmC,EAAA;AAAA,MACjC,KAAO,EAAA,gBAAA;AAAA,MACP,QAAU,EAAA;AAAA;AACZ,GACF;AAAA,EACA,aAAe,EAAA;AAAA,IACb,OAAS,EAAA;AAAA,MACP,QAAU,EAAA;AAAA;AACZ;AAEJ,CAAE,CAAA,CAAA;AAWK,MAAM,iBAAiB,CAAC;AAAA,EAC7B,aAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF,CAA2B,KAAA;AACzB,EAAA,MAAM,WAAW,WAAY,EAAA;AAC7B,EAAA,MAAM,UAAU,SAAU,EAAA;AAC1B,EAAA,MAAM,OAAO,wBAAyB,EAAA;AACtC,EAAA,MAAM,CAAC,WAAa,EAAA,cAAc,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AAC/D,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AACjE,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AACrE,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,IAAIA,cAAM,CAAA,QAAA,CAAkB,CAAC,QAAQ,CAAA;AACzE,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AACzE,EAAA,MAAM,CAAC,oBAAsB,EAAA,uBAAuB,CAClD,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AAC/B,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,CAAI,GAAAA,cAAA,CAAM,SAAuB,IAAI,CAAA;AAC3D,EAAA,MAAM,CAAC,oBAAsB,EAAA,uBAAuB,CAClD,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AAC3B,EAAA,MAAM,CAAC,iBAAmB,EAAA,oBAAoB,CAC5C,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AAC/B,EAAA,MAAM,EAAE,OAAS,EAAA,YAAA,EAAc,iBAAiB,iBAAkB,EAAA,GAChE,0BAA0B,IAAI,CAAA;AAEhC,EAAM,MAAA;AAAA,IACJ,WAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,MACE,wBAAyB,EAAA;AAG7B,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAI,IAAA,OAAA,IAAW,iBAAiB,IAAM,EAAA;AACpC,MAAA,iBAAA,CAAkB,YAAY,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,YAAc,EAAA,OAAO,CAAC,CAAA;AAE1B,EAAA,MAAM,cAAc,cAAe,EAAA;AAEnC,EAAM,MAAA;AAAA,IACJ,IAAA,EAAM,gBAAgB,EAAC;AAAA,IACvB,SAAA;AAAA,IACA;AAAA,MACE,gBAAiB,EAAA;AACrB,EAAA,MAAM,EAAE,WAAA,EAAa,kBAAmB,EAAA,GAAI,qBAAsB,EAAA;AAClE,EAAA,MAAM,EAAE,OAAA,EAAS,eAAgB,EAAA,GAAI,6BAA8B,EAAA;AACnE,EAAA,MAAM,gBAAgB,iBAAkB,EAAA;AACxC,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAI,IAAA,IAAA,IAAQ,YAAiB,KAAA,IAAA,IAAQ,OAAS,EAAA;AAC5C,MAAA,iBAAA,CAAkB,oBAAoB,CAAA;AACtC,MAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA;AACxB,KACC,CAAC,IAAA,EAAM,OAAS,EAAA,YAAA,EAAc,iBAAiB,CAAC,CAAA;AAEnD,EAAAA,cAAA,CAAM,UAAU,MAAM;AAEpB,IAAA,IACE,CAAC,SACD,IAAA,CAAC,gBACD,aAAc,CAAA,MAAA,KAAW,KACzB,YACA,EAAA;AACA,MAAkB,iBAAA,EAAA;AAAA;AACpB,KACC,CAAC,SAAA,EAAW,cAAc,aAAe,EAAA,YAAA,EAAc,iBAAiB,CAAC,CAAA;AAE5E,EAAAA,cAAA,CAAM,UAAU,MAAM;AAEpB,IAAA,IAAI,cAAgB,EAAA;AAClB,MAAA,eAAA,CAAgB,cAAc,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,cAAgB,EAAA,eAAe,CAAC,CAAA;AAEpC,EAAM,MAAA,OAAA,GAAU,CAAC,OAAoB,KAAA;AACnC,IAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,GAC3B;AAEA,EAAM,MAAA,UAAA,GAAa,CAAC,OAAoB,KAAA;AACtC,IAAA,uBAAA,CAAwB,KAAK,CAAA;AAC7B,IAAgB,eAAA,CAAA,CAAA,kBAAA,EAAqB,OAAO,CAAE,CAAA,CAAA;AAC9C,IAAA,WAAA,CAAY,iBAAkB,CAAA;AAAA,MAC5B,QAAA,EAAU,CAAC,eAAe;AAAA,KAC3B,CAAA;AACD,IAAA,WAAA,CAAY,iBAAkB,CAAA;AAAA,MAC5B,QAAA,EAAU,CAAC,sBAAA,EAAwB,cAAc;AAAA,KAClD,CAAA;AACD,IAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,GACzB;AAEA,EAAA,MAAM,EAAE,oBAAA,EAAsB,iBAAmB,EAAA,iBAAA,EAC/C,GAAA,uBAAA;AAAA,IACE,cAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AAEF,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,CAC1B,GAAAA,cAAA,CAAM,SAAyB,oBAAoB,CAAA;AAErD,EAAM,MAAA,WAAA,GAAc,CAAC,OAA6B,KAAA;AAChD,IAAA,IAAI,mBAAmB,oBAAsB,EAAA;AAC3C,MAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA;AAEzB,IAAA,eAAA;AAAA,MACE,sBAAsB,MAAM,CAAA,8BAAA;AAAA,KAC9B;AACA,IAAA,iBAAA,CAAkB,OAAQ,CAAA,QAAA,EAAY,EAAA,cAAA,CAAe,YAAY,CAAC,CAAA;AAClE,IAAA,uBAAA,CAAwB,IAAI,CAAA;AAC5B,IAAA,eAAA,CAAgB,EAAE,CAAA;AAAA,GACpB;AAEA,EAAM,MAAA,SAAA,GAAYA,cAAM,CAAA,WAAA,CAAY,MAAM;AACxC,IAAA,CAAC,YAAY;AACX,MAAA,IAAI,mBAAmB,oBAAsB,EAAA;AAC3C,QAAA,WAAA,CAAY,EAAE,CAAA;AACd,QAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,QAAe,cAAA,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAChC,QAAA,iBAAA,CAAkB,oBAAoB,CAAA;AACtC,QAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA;AACxB,KACC,GAAA;AAAA,GACF,EAAA;AAAA,IACD,cAAA;AAAA,IACA,iBAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAM,MAAA,eAAA,GAAkB,CAAC,eAA4B,KAAA;AACnD,IAAA,uBAAA,CAAwB,eAAe,CAAA;AACvC,IAAA,oBAAA,CAAqB,IAAI,CAAA;AAAA,GAC3B;AAEA,EAAM,MAAA,wBAAA,GAA2BA,cAAM,CAAA,WAAA,CAAY,MAAM;AACvD,IAAA,CAAC,YAAY;AACX,MAAI,IAAA;AACF,QAAA,MAAM,kBAAmB,CAAA;AAAA,UACvB,eAAiB,EAAA,oBAAA;AAAA,UACjB,eAAiB,EAAA;AAAA,SAClB,CAAA;AACD,QAAA,IAAI,yBAAyB,YAAc,EAAA;AACzC,UAAU,SAAA,EAAA;AACV,UAAkB,iBAAA,EAAA;AAAA;AAEpB,QAAA,oBAAA,CAAqB,KAAK,CAAA;AAAA,eACnB,CAAG,EAAA;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AACd,QAAA,QAAA,CAAS,CAAC,CAAA;AAAA;AACZ,KACC,GAAA;AAAA,GACF,EAAA;AAAA,IACD,kBAAA;AAAA,IACA,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,yBAAyBA,cAAM,CAAA,WAAA;AAAA,IACnC,CAAC,mBAA8C,MAAA;AAAA,MAC7C,SACE,kBAAAA,cAAA,CAAA,aAAA;AAAA,QAAC,YAAA;AAAA,QAAA;AAAA,UACC,YAAY,CAAC,eAAA;AAAA,UACb,OAAS,EAAA,MAAM,eAAgB,CAAA,mBAAA,CAAoB,eAAe;AAAA,SAAA;AAAA,QACnE;AAAA;AAED,KAEJ,CAAA;AAAA,IACA,CAAC,eAAe;AAAA,GAClB;AACA,EAAA,MAAM,mBAAsB,GAAA,qBAAA;AAAA,IAC1B,aAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,sBAAsBA,cAAM,CAAA,WAAA;AAAA,IAChC,CAAC,WAAwB,KAAA;AACvB,MAAA,MAAM,qBAAwB,GAAA,MAAA,CAAO,OAAQ,CAAA,mBAAmB,CAAE,CAAA,MAAA;AAAA,QAChE,CAAC,GAAA,EAAK,CAAC,GAAA,EAAK,KAAK,CAAM,KAAA;AACrB,UAAA,MAAM,gBAAgB,KAAM,CAAA,MAAA;AAAA,YAAO,CAAA,IAAA,KACjC,IAAK,CAAA,IAAA,CACF,iBAAkB,CAAA,OAAO,EACzB,QAAS,CAAA,WAAA,CAAY,iBAAkB,CAAA,OAAO,CAAC;AAAA,WACpD;AACA,UAAI,IAAA,aAAA,CAAc,SAAS,CAAG,EAAA;AAC5B,YAAA,GAAA,CAAI,GAAG,CAAI,GAAA,aAAA;AAAA;AAEb,UAAO,OAAA,GAAA;AAAA,SACT;AAAA,QACA;AAAC,OACH;AACA,MAAO,OAAA,qBAAA;AAAA,KACT;AAAA,IACA,CAAC,mBAAmB;AAAA,GACtB;AAEA,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAA,WAAA,CAAY,oBAAoB,CAAA;AAAA,GAClC,EAAG,CAAC,oBAAoB,CAAC,CAAA;AAEzB,EAAA,MAAM,qBAAqBA,cAAM,CAAA,WAAA;AAAA,IAC/B,CACE,GACA,YACG,KAAA;AACH,MAAA,iBAAA,CAAkB,KAAK,CAAA;AACvB,MAAA,iBAAA,CAAkB,CAAC,IAAiB,KAAA;AAClC,QAAA,IAAI,SAAS,YAAc,EAAA;AACzB,UAAA,OAAO,OAAO,YAAY,CAAA;AAAA;AAE5B,QAAO,OAAA,IAAA;AAAA,OACR,CAAA;AACD,MAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,MAAe,cAAA,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAChC,MAAA,iBAAA,CAAkB,SAAS,cAAe,EAAA;AAAA,KAC5C;AAAA,IACA,CAAC,iBAAA,EAAmB,cAAgB,EAAA,eAAA,EAAiB,iBAAiB;AAAA,GACxE;AAEA,EAAM,MAAA,iBAAA,GAAoB,CAAC,CAAC,aAAc,CAAA,IAAA;AAAA,IACxC,CAAC,CAA2B,KAAA,CAAA,CAAE,eAAoB,KAAA;AAAA,GACpD;AAEA,EAAA,MAAM,cACH,GAAA,cAAA,IAAkB,oBAAqB,CAAA,MAAA,KAAW,CAClD,IAAA,CAAC,iBAAqB,IAAA,oBAAA,CAAqB,MAAW,KAAA,CAAA,GACnD,aAAe,EAAA,GAAA,CAAI,CAAAC,OAAW,MAAA;AAAA,IAC5B,OAAOA,OAAO,CAAA,KAAA;AAAA,IACd,SAASA,OAAO,CAAA,OAAA;AAAA,IAChB,SAAS,MAAM;AACb,MAAA,WAAA,CAAYA,QAAO,OAAO,CAAA;AAAA;AAC5B,GACF,CAAE,IACF,EAAC;AAEP,EAAA,MAAM,YAAe,GAAAD,cAAA,CAAM,WAAY,CAAA,CAAC,KAAkB,KAAA;AACxD,IAAA,cAAA,CAAe,KAAK,CAAA;AAAA,GACtB,EAAG,EAAE,CAAA;AAEL,EAAM,MAAA,cAAA,GAAiBA,cAAM,CAAA,WAAA,CAAY,MAAM;AAC7C,IAAgB,eAAA,CAAA,CAAA,MAAA,KAAU,CAAC,MAAM,CAAA;AAAA,GACnC,EAAG,EAAE,CAAA;AAEL,EAAM,MAAA,YAAA,GAAe,CAAC,IAAA,EAAc,KAAqB,KAAA;AACvD,IAAA,KAAA,CAAM,cAAe,EAAA;AACrB,IAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,GACvB;AAEA,EAAM,MAAA,gBAAA,GAAmB,CAAC,IAA0B,KAAA;AAClD,IAAA,IAAA,CAAK,QAAQ,CAAc,UAAA,KAAA;AACzB,MAAI,IAAA,CAAC,CAAC,UAAW,CAAA,MAAA,CAAO,KAAK,CAAK,CAAA,KAAA,CAAA,CAAE,IAAS,KAAA,mBAAmB,CAAG,EAAA;AACjE,QAAA,YAAA,CAAa,IAAI,CAAA;AACjB,QAAe,cAAA,CAAA;AAAA,UACb,OACE,EAAA;AAAA,SACH,CAAA;AAAA;AACH,KACD,CAAA;AAAA,GACH;AAEA,EAAA,IAAI,KAAO,EAAA;AACT,IAAA,oDACG,GAAI,EAAA,EAAA,OAAA,EAAS,qBACXA,cAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAc,CAC5B,CAAA;AAAA;AAIJ,EAAA,mFAEK,iBACC,oBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,MAAQ,EAAA,iBAAA;AAAA,MACR,OAAA,EAAS,MAAM,oBAAA,CAAqB,KAAK,CAAA;AAAA,MACzC,SAAW,EAAA;AAAA;AAAA,GAGf,kBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,aAAa,kBAAmB,CAAA,QAAA;AAAA,MAChC,WAAW,OAAQ,CAAA;AAAA,KAAA;AAAA,iDAElB,aAAc,EAAA,EAAA,SAAA,EAAW,OAAQ,CAAA,MAAA,EAAA,+CAC/B,iBACC,EAAA,IAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,eAAe,EAAA,YAAA;AAAA,QACf,YAAc,EAAA,MAAM,eAAgB,CAAA,CAAC,YAAY,CAAA;AAAA,QACjD,WAAW,OAAQ,CAAA;AAAA;AAAA,KAErB,kBAAAA,cAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,SAAA,EAAW,QAAQ,WACrC,EAAA,kBAAAA,cAAA,CAAA,aAAA,CAAC,KAAM,EAAA,EAAA,YAAA,EAAa,MAAK,IAAK,EAAA,KAAA,EAAA,EAAM,sBAEpC,CACF,CACF,CAEA,kBAAAA,cAAA,CAAA,aAAA;AAAA,MAAC,uBAAA;AAAA,MAAA;AAAA,QACC,aAAA;AAAA,QACA,mBAAA,EAAqB,CAAQ,IAAA,KAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,QACrD;AAAA;AAAA,KAEJ,CAAA;AAAA,oBACAA,cAAA,CAAA,aAAA;AAAA,MAAC,6BAAA;AAAA,MAAA;AAAA,QACC,uBAAyB,EAAA,EAAE,WAAa,EAAA,IAAA,EAAM,SAAS,OAAQ,EAAA;AAAA,QAC/D,kBAAkB,EAAA,IAAA;AAAA,QAClB,aAAa,kBAAmB,CAAA,QAAA;AAAA,QAChC,cAAA;AAAA,QACA,YAAA;AAAA,QACA,eAAA;AAAA,QACA,YAAc,EAAA,cAAA;AAAA,QACd,kBAAA;AAAA,QACA,aAAA,EAAe,oBAAoB,WAAW,CAAA;AAAA,QAC9C,SAAA,EAAW,iBAAiB,SAAY,GAAA,SAAA;AAAA,QACxC,qBAAuB,EAAA,YAAA;AAAA,QACvB,sBAAuB,EAAA,0BAAA;AAAA,QACvB,aACE,kBAAAA,cAAA,CAAA,aAAA;AAAA,UAAC,YAAA;AAAA,UAAA;AAAA,YACC,YAAY,CAAC,CAAA,EAAG,IAAS,KAAA,YAAA,CAAa,MAAM,CAAC,CAAA;AAAA,YAC7C,aAAa,kBAAmB,CAAA,QAAA;AAAA,YAChC,QAAS,EAAA,wFAAA;AAAA,YACT,gBAAkB,EAAA,kBAAA;AAAA,YAClB;AAAA,WAAA;AAAA,UAEC,aAAa,WAAY,CAAA,OAAA,iDACvB,KAAI,EAAA,EAAA,SAAA,EAAW,QAAQ,cACtB,EAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,YAAA;AAAA,YAAA;AAAA,cACC,SAAU,EAAA,IAAA;AAAA,cACV,KAAM,EAAA,oBAAA;AAAA,cACN,OAAA,EAAS,YAAY,IAAQ,IAAA,QAAA;AAAA,cAC7B,QAAQ,EAAA,IAAA;AAAA,cACR,SAAS,MAAM,cAAA,CAAe,EAAE,OAAA,EAAS,MAAM;AAAA,aAAA;AAAA,YAE9C,WAAY,CAAA;AAAA,WAEjB,CAAA;AAAA,uDAGD,cACC,EAAA,IAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,iBAAA;AAAA,YAAA;AAAA,cACC,QAAA;AAAA,cACA,QAAA;AAAA,cACA,cAAA;AAAA,cACA,YAAA;AAAA,cACA,GAAK,EAAA,iBAAA;AAAA,cACL;AAAA;AAAA,WAEJ,CAAA;AAAA,uDACC,aAAc,EAAA,EAAA,SAAA,EAAW,QAAQ,MAChC,EAAA,kBAAAA,cAAA,CAAA,aAAA,CAAC,iBAAY,CACb,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,aAAe,EAAA,WAAA;AAAA,cACf,oBAAA;AAAA,cACA,eAAe,EAAA,IAAA;AAAA,cACf,YAAA;AAAA,cACA,mBAAmB,EAAA,IAAA;AAAA,cACnB,WAAa,EAAA;AAAA,gBACX,MAAQ,EAAA;AAAA,kBACN,WAAa,EAAA;AAAA;AACf,eACF;AAAA,cACA,gBAAkB,EAAA,kBAAA;AAAA,cAClB,gBAAA;AAAA,cACA,WAAY,EAAA;AAAA;AAAA,WACd,+CACC,eAAiB,EAAA,EAAA,GAAG,iBAAiB,OAAQ,CAAA,aAAa,GAAG,CAChE;AAAA;AACF;AAAA;AAEJ,GACF,kBACCA,cAAA,CAAA,aAAA,CAAA,UAAA,EAAA,IAAW,CACd,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"LightSpeedChat.esm.js","sources":["../../src/components/LightSpeedChat.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 React from 'react';\nimport { FileRejection } from 'react-dropzone/.';\n\nimport { ErrorPanel } from '@backstage/core-components';\n\nimport { Box, makeStyles } from '@material-ui/core';\nimport {\n Chatbot,\n ChatbotAlert,\n ChatbotContent,\n ChatbotDisplayMode,\n ChatbotFooter,\n ChatbotFootnote,\n ChatbotHeader,\n ChatbotHeaderMain,\n ChatbotHeaderMenu,\n ChatbotHeaderTitle,\n FileDropZone,\n MessageBar,\n MessageProps,\n} from '@patternfly/chatbot';\nimport ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';\nimport { DropdownItem, DropEvent, Title } from '@patternfly/react-core';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { supportedFileTypes, TEMP_CONVERSATION_ID } from '../const';\nimport {\n useBackstageUserIdentity,\n useConversationMessages,\n useConversations,\n useDeleteConversation,\n useIsMobile,\n useLastOpenedConversation,\n useLightspeedDeletePermission,\n} from '../hooks';\nimport { useWelcomePrompts } from '../hooks/useWelcomePrompts';\nimport { ConversationSummary } from '../types';\nimport { getAttachments } from '../utils/attachment-utils';\nimport {\n getCategorizeMessages,\n getFootnoteProps,\n} from '../utils/lightspeed-chatbox-utils';\nimport Attachment from './Attachment';\nimport { useFileAttachmentContext } from './AttachmentContext';\nimport { DeleteModal } from './DeleteModal';\nimport FilePreview from './FilePreview';\nimport { LightspeedChatBox } from './LightspeedChatBox';\nimport { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader';\n\nconst useStyles = makeStyles(theme => ({\n body: {\n // remove default margin and padding from common elements\n '& h1, & h2, & h3, & h4, & h5, & h6, & p, & ul, & ol, & li': {\n margin: 0,\n padding: 0,\n },\n },\n header: {\n padding: `${theme.spacing(3)}px !important`,\n },\n errorContainer: {\n padding: theme.spacing(3),\n },\n headerMenu: {\n // align hamburger icon with title\n '& .pf-v6-c-button': {\n display: 'flex',\n alignItems: 'center',\n },\n },\n headerTitle: {\n justifyContent: 'left !important',\n },\n footer: {\n '&>.pf-chatbot__footer-container': {\n width: '95% !important',\n maxWidth: 'unset !important',\n },\n },\n footerPopover: {\n '& img': {\n maxWidth: '100%',\n },\n },\n}));\n\ntype LightspeedChatProps = {\n selectedModel: string;\n userName?: string;\n avatar?: string;\n profileLoading: boolean;\n handleSelectedModel: (item: string) => void;\n models: { label: string; value: string }[];\n};\n\nexport const LightspeedChat = ({\n selectedModel,\n userName,\n avatar,\n profileLoading,\n handleSelectedModel,\n models,\n}: LightspeedChatProps) => {\n const isMobile = useIsMobile();\n const classes = useStyles();\n const user = useBackstageUserIdentity();\n const [filterValue, setFilterValue] = React.useState<string>('');\n const [announcement, setAnnouncement] = React.useState<string>('');\n const [conversationId, setConversationId] = React.useState<string>('');\n const [isDrawerOpen, setIsDrawerOpen] = React.useState<boolean>(!isMobile);\n const [newChatCreated, setNewChatCreated] = React.useState<boolean>(false);\n const [isSendButtonDisabled, setIsSendButtonDisabled] =\n React.useState<boolean>(false);\n const [error, setError] = React.useState<Error | null>(null);\n const [targetConversationId, setTargetConversationId] =\n React.useState<string>('');\n const [isDeleteModalOpen, setIsDeleteModalOpen] =\n React.useState<boolean>(false);\n const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } =\n useLastOpenedConversation(user);\n\n const {\n uploadError,\n showAlert,\n fileContents,\n setShowAlert,\n setFileContents,\n handleFileUpload,\n setUploadError,\n } = useFileAttachmentContext();\n\n // Sync conversationId with lastOpenedId whenever lastOpenedId changes\n React.useEffect(() => {\n if (isReady && lastOpenedId !== null) {\n setConversationId(lastOpenedId);\n }\n }, [lastOpenedId, isReady]);\n\n const queryClient = useQueryClient();\n\n const {\n data: conversations = [],\n isLoading,\n isRefetching,\n } = useConversations();\n const { mutateAsync: deleteConversation } = useDeleteConversation();\n const { allowed: hasDeleteAccess } = useLightspeedDeletePermission();\n const samplePrompts = useWelcomePrompts();\n React.useEffect(() => {\n if (user && lastOpenedId === null && isReady) {\n setConversationId(TEMP_CONVERSATION_ID);\n setNewChatCreated(true);\n }\n }, [user, isReady, lastOpenedId, setConversationId]);\n\n React.useEffect(() => {\n // Clear last opened conversationId when there are no conversations.\n if (\n !isLoading &&\n !isRefetching &&\n conversations.length === 0 &&\n lastOpenedId\n ) {\n clearLastOpenedId();\n }\n }, [isLoading, isRefetching, conversations, lastOpenedId, clearLastOpenedId]);\n\n React.useEffect(() => {\n // Update last opened conversation whenever `conversationId` changes\n if (conversationId) {\n setLastOpenedId(conversationId);\n }\n }, [conversationId, setLastOpenedId]);\n\n const onStart = (conv_id: string) => {\n setConversationId(conv_id);\n };\n\n const onComplete = (message: string) => {\n setIsSendButtonDisabled(false);\n setAnnouncement(`Message from Bot: ${message}`);\n queryClient.invalidateQueries({\n queryKey: ['conversations'],\n });\n queryClient.invalidateQueries({\n queryKey: ['conversationMessages', conversationId],\n });\n setNewChatCreated(false);\n };\n\n const { conversationMessages, handleInputPrompt, scrollToBottomRef } =\n useConversationMessages(\n conversationId,\n userName,\n selectedModel,\n avatar,\n onComplete,\n onStart,\n );\n\n const [messages, setMessages] =\n React.useState<MessageProps[]>(conversationMessages);\n\n const sendMessage = (message: string | number) => {\n if (conversationId !== TEMP_CONVERSATION_ID) {\n setNewChatCreated(false);\n }\n setAnnouncement(\n `Message from User: ${prompt}. Message from Bot is loading.`,\n );\n handleInputPrompt(message.toString(), getAttachments(fileContents));\n setIsSendButtonDisabled(true);\n setFileContents([]);\n };\n\n const onNewChat = React.useCallback(() => {\n (async () => {\n if (conversationId !== TEMP_CONVERSATION_ID) {\n setMessages([]);\n setFileContents([]);\n setUploadError({ message: null });\n setConversationId(TEMP_CONVERSATION_ID);\n setNewChatCreated(true);\n }\n })();\n }, [\n conversationId,\n setConversationId,\n setMessages,\n setUploadError,\n setFileContents,\n ]);\n\n const openDeleteModal = (conversation_id: string) => {\n setTargetConversationId(conversation_id);\n setIsDeleteModalOpen(true);\n };\n\n const handleDeleteConversation = React.useCallback(() => {\n (async () => {\n try {\n await deleteConversation({\n conversation_id: targetConversationId,\n invalidateCache: false,\n });\n if (targetConversationId === lastOpenedId) {\n onNewChat();\n clearLastOpenedId();\n }\n setIsDeleteModalOpen(false);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n setError(e);\n }\n })();\n }, [\n deleteConversation,\n clearLastOpenedId,\n lastOpenedId,\n onNewChat,\n targetConversationId,\n ]);\n\n const additionalMessageProps = React.useCallback(\n (conversationSummary: ConversationSummary) => ({\n menuItems: (\n <DropdownItem\n isDisabled={!hasDeleteAccess}\n onClick={() => openDeleteModal(conversationSummary.conversation_id)}\n >\n Delete\n </DropdownItem>\n ),\n }),\n [hasDeleteAccess],\n );\n const categorizedMessages = getCategorizeMessages(\n conversations,\n additionalMessageProps,\n );\n\n const filterConversations = React.useCallback(\n (targetValue: string) => {\n const filteredConversations = Object.entries(categorizedMessages).reduce(\n (acc, [key, items]) => {\n const filteredItems = items.filter(item =>\n item.text\n .toLocaleLowerCase('en-US')\n .includes(targetValue.toLocaleLowerCase('en-US')),\n );\n if (filteredItems.length > 0) {\n acc[key] = filteredItems;\n }\n return acc;\n },\n {} as any,\n );\n return filteredConversations;\n },\n [categorizedMessages],\n );\n\n React.useEffect(() => {\n setMessages(conversationMessages);\n }, [conversationMessages]);\n\n const onSelectActiveItem = React.useCallback(\n (\n _: React.MouseEvent | undefined,\n selectedItem: string | number | undefined,\n ) => {\n setNewChatCreated(false);\n setConversationId((c_id: string) => {\n if (c_id !== selectedItem) {\n return String(selectedItem);\n }\n return c_id;\n });\n setFileContents([]);\n setUploadError({ message: null });\n scrollToBottomRef.current?.scrollToBottom();\n },\n [setConversationId, setUploadError, setFileContents, scrollToBottomRef],\n );\n\n const conversationFound = !!conversations.find(\n (c: ConversationSummary) => c.conversation_id === conversationId,\n );\n\n const welcomePrompts =\n (newChatCreated && conversationMessages.length === 0) ||\n (!conversationFound && conversationMessages.length === 0)\n ? samplePrompts?.map(prompt => ({\n title: prompt.title,\n message: prompt.message,\n onClick: () => {\n sendMessage(prompt.message);\n },\n }))\n : [];\n\n const handleFilter = React.useCallback((value: string) => {\n setFilterValue(value);\n }, []);\n\n const onDrawerToggle = React.useCallback(() => {\n setIsDrawerOpen(isOpen => !isOpen);\n }, []);\n\n const handleAttach = (data: File[], event: DropEvent) => {\n event.preventDefault();\n handleFileUpload(data);\n };\n\n const onAttachRejected = (data: FileRejection[]) => {\n data.forEach(attachment => {\n if (!!attachment.errors.find(e => e.code === 'file-invalid-type')) {\n setShowAlert(true);\n setUploadError({\n message:\n 'Unsupported file type. Supported types are: .txt, .yaml, .json and .xml.',\n });\n }\n });\n };\n\n if (error) {\n return (\n <Box padding={1}>\n <ErrorPanel error={error} />\n </Box>\n );\n }\n\n return (\n <>\n {isDeleteModalOpen && (\n <DeleteModal\n isOpen={isDeleteModalOpen}\n onClose={() => setIsDeleteModalOpen(false)}\n onConfirm={handleDeleteConversation}\n />\n )}\n <Chatbot\n displayMode={ChatbotDisplayMode.embedded}\n className={classes.body}\n >\n <ChatbotHeader className={classes.header}>\n <ChatbotHeaderMain>\n <ChatbotHeaderMenu\n aria-expanded={isDrawerOpen}\n onMenuToggle={() => setIsDrawerOpen(!isDrawerOpen)}\n className={classes.headerMenu}\n />\n <ChatbotHeaderTitle className={classes.headerTitle}>\n <Title headingLevel=\"h1\" size=\"3xl\">\n Developer Lightspeed\n </Title>\n </ChatbotHeaderTitle>\n </ChatbotHeaderMain>\n\n <LightspeedChatBoxHeader\n selectedModel={selectedModel}\n handleSelectedModel={item => handleSelectedModel(item)}\n models={models}\n />\n </ChatbotHeader>\n <ChatbotConversationHistoryNav\n drawerPanelContentProps={{ isResizable: true, minSize: '200px' }}\n reverseButtonOrder\n displayMode={ChatbotDisplayMode.embedded}\n onDrawerToggle={onDrawerToggle}\n isDrawerOpen={isDrawerOpen}\n setIsDrawerOpen={setIsDrawerOpen}\n activeItemId={conversationId}\n onSelectActiveItem={onSelectActiveItem}\n conversations={filterConversations(filterValue)}\n onNewChat={newChatCreated ? undefined : onNewChat}\n handleTextInputChange={handleFilter}\n searchInputPlaceholder=\"Search previous chats...\"\n drawerContent={\n <FileDropZone\n onFileDrop={(e, data) => handleAttach(data, e)}\n displayMode={ChatbotDisplayMode.embedded}\n infoText=\"Supported file types are: .txt, .yaml, .json and .xml. The maximum file size is 25 MB.\"\n allowedFileTypes={supportedFileTypes}\n onAttachRejected={onAttachRejected}\n >\n {showAlert && uploadError.message && (\n <div className={classes.errorContainer}>\n <ChatbotAlert\n component=\"h4\"\n title=\"File upload failed\"\n variant={uploadError.type ?? 'danger'}\n isInline\n onClose={() => setUploadError({ message: null })}\n >\n {uploadError.message}\n </ChatbotAlert>\n </div>\n )}\n\n <ChatbotContent>\n <LightspeedChatBox\n userName={userName}\n messages={messages}\n profileLoading={profileLoading}\n announcement={announcement}\n ref={scrollToBottomRef}\n welcomePrompts={welcomePrompts}\n conversationId={conversationId}\n isStreaming={isSendButtonDisabled}\n />\n </ChatbotContent>\n <ChatbotFooter className={classes.footer}>\n <FilePreview />\n <MessageBar\n onSendMessage={sendMessage}\n isSendButtonDisabled={isSendButtonDisabled}\n hasAttachButton\n handleAttach={handleAttach}\n hasMicrophoneButton\n buttonProps={{\n attach: {\n inputTestId: 'attachment-input',\n },\n }}\n allowedFileTypes={supportedFileTypes}\n onAttachRejected={onAttachRejected}\n placeholder=\"Send a message and optionally upload a JSON, YAML, TXT, or XML file...\"\n />\n <ChatbotFootnote {...getFootnoteProps(classes.footerPopover)} />\n </ChatbotFooter>\n </FileDropZone>\n }\n />\n </Chatbot>\n <Attachment />\n </>\n );\n};\n"],"names":["React","prompt"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,IAAM,EAAA;AAAA;AAAA,IAEJ,2DAA6D,EAAA;AAAA,MAC3D,MAAQ,EAAA,CAAA;AAAA,MACR,OAAS,EAAA;AAAA;AACX,GACF;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,OAAS,EAAA,CAAA,EAAG,KAAM,CAAA,OAAA,CAAQ,CAAC,CAAC,CAAA,aAAA;AAAA,GAC9B;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,OAAA,EAAS,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA,GAC1B;AAAA,EACA,UAAY,EAAA;AAAA;AAAA,IAEV,mBAAqB,EAAA;AAAA,MACnB,OAAS,EAAA,MAAA;AAAA,MACT,UAAY,EAAA;AAAA;AACd,GACF;AAAA,EACA,WAAa,EAAA;AAAA,IACX,cAAgB,EAAA;AAAA,GAClB;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,iCAAmC,EAAA;AAAA,MACjC,KAAO,EAAA,gBAAA;AAAA,MACP,QAAU,EAAA;AAAA;AACZ,GACF;AAAA,EACA,aAAe,EAAA;AAAA,IACb,OAAS,EAAA;AAAA,MACP,QAAU,EAAA;AAAA;AACZ;AAEJ,CAAE,CAAA,CAAA;AAWK,MAAM,iBAAiB,CAAC;AAAA,EAC7B,aAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF,CAA2B,KAAA;AACzB,EAAA,MAAM,WAAW,WAAY,EAAA;AAC7B,EAAA,MAAM,UAAU,SAAU,EAAA;AAC1B,EAAA,MAAM,OAAO,wBAAyB,EAAA;AACtC,EAAA,MAAM,CAAC,WAAa,EAAA,cAAc,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AAC/D,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AACjE,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AACrE,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,IAAIA,cAAM,CAAA,QAAA,CAAkB,CAAC,QAAQ,CAAA;AACzE,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AACzE,EAAA,MAAM,CAAC,oBAAsB,EAAA,uBAAuB,CAClD,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AAC/B,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,CAAI,GAAAA,cAAA,CAAM,SAAuB,IAAI,CAAA;AAC3D,EAAA,MAAM,CAAC,oBAAsB,EAAA,uBAAuB,CAClD,GAAAA,cAAA,CAAM,SAAiB,EAAE,CAAA;AAC3B,EAAA,MAAM,CAAC,iBAAmB,EAAA,oBAAoB,CAC5C,GAAAA,cAAA,CAAM,SAAkB,KAAK,CAAA;AAC/B,EAAA,MAAM,EAAE,OAAS,EAAA,YAAA,EAAc,iBAAiB,iBAAkB,EAAA,GAChE,0BAA0B,IAAI,CAAA;AAEhC,EAAM,MAAA;AAAA,IACJ,WAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,MACE,wBAAyB,EAAA;AAG7B,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAI,IAAA,OAAA,IAAW,iBAAiB,IAAM,EAAA;AACpC,MAAA,iBAAA,CAAkB,YAAY,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,YAAc,EAAA,OAAO,CAAC,CAAA;AAE1B,EAAA,MAAM,cAAc,cAAe,EAAA;AAEnC,EAAM,MAAA;AAAA,IACJ,IAAA,EAAM,gBAAgB,EAAC;AAAA,IACvB,SAAA;AAAA,IACA;AAAA,MACE,gBAAiB,EAAA;AACrB,EAAA,MAAM,EAAE,WAAA,EAAa,kBAAmB,EAAA,GAAI,qBAAsB,EAAA;AAClE,EAAA,MAAM,EAAE,OAAA,EAAS,eAAgB,EAAA,GAAI,6BAA8B,EAAA;AACnE,EAAA,MAAM,gBAAgB,iBAAkB,EAAA;AACxC,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAI,IAAA,IAAA,IAAQ,YAAiB,KAAA,IAAA,IAAQ,OAAS,EAAA;AAC5C,MAAA,iBAAA,CAAkB,oBAAoB,CAAA;AACtC,MAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA;AACxB,KACC,CAAC,IAAA,EAAM,OAAS,EAAA,YAAA,EAAc,iBAAiB,CAAC,CAAA;AAEnD,EAAAA,cAAA,CAAM,UAAU,MAAM;AAEpB,IAAA,IACE,CAAC,SACD,IAAA,CAAC,gBACD,aAAc,CAAA,MAAA,KAAW,KACzB,YACA,EAAA;AACA,MAAkB,iBAAA,EAAA;AAAA;AACpB,KACC,CAAC,SAAA,EAAW,cAAc,aAAe,EAAA,YAAA,EAAc,iBAAiB,CAAC,CAAA;AAE5E,EAAAA,cAAA,CAAM,UAAU,MAAM;AAEpB,IAAA,IAAI,cAAgB,EAAA;AAClB,MAAA,eAAA,CAAgB,cAAc,CAAA;AAAA;AAChC,GACC,EAAA,CAAC,cAAgB,EAAA,eAAe,CAAC,CAAA;AAEpC,EAAM,MAAA,OAAA,GAAU,CAAC,OAAoB,KAAA;AACnC,IAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,GAC3B;AAEA,EAAM,MAAA,UAAA,GAAa,CAAC,OAAoB,KAAA;AACtC,IAAA,uBAAA,CAAwB,KAAK,CAAA;AAC7B,IAAgB,eAAA,CAAA,CAAA,kBAAA,EAAqB,OAAO,CAAE,CAAA,CAAA;AAC9C,IAAA,WAAA,CAAY,iBAAkB,CAAA;AAAA,MAC5B,QAAA,EAAU,CAAC,eAAe;AAAA,KAC3B,CAAA;AACD,IAAA,WAAA,CAAY,iBAAkB,CAAA;AAAA,MAC5B,QAAA,EAAU,CAAC,sBAAA,EAAwB,cAAc;AAAA,KAClD,CAAA;AACD,IAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,GACzB;AAEA,EAAA,MAAM,EAAE,oBAAA,EAAsB,iBAAmB,EAAA,iBAAA,EAC/C,GAAA,uBAAA;AAAA,IACE,cAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AAEF,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,CAC1B,GAAAA,cAAA,CAAM,SAAyB,oBAAoB,CAAA;AAErD,EAAM,MAAA,WAAA,GAAc,CAAC,OAA6B,KAAA;AAChD,IAAA,IAAI,mBAAmB,oBAAsB,EAAA;AAC3C,MAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA;AAEzB,IAAA,eAAA;AAAA,MACE,sBAAsB,MAAM,CAAA,8BAAA;AAAA,KAC9B;AACA,IAAA,iBAAA,CAAkB,OAAQ,CAAA,QAAA,EAAY,EAAA,cAAA,CAAe,YAAY,CAAC,CAAA;AAClE,IAAA,uBAAA,CAAwB,IAAI,CAAA;AAC5B,IAAA,eAAA,CAAgB,EAAE,CAAA;AAAA,GACpB;AAEA,EAAM,MAAA,SAAA,GAAYA,cAAM,CAAA,WAAA,CAAY,MAAM;AACxC,IAAA,CAAC,YAAY;AACX,MAAA,IAAI,mBAAmB,oBAAsB,EAAA;AAC3C,QAAA,WAAA,CAAY,EAAE,CAAA;AACd,QAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,QAAe,cAAA,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAChC,QAAA,iBAAA,CAAkB,oBAAoB,CAAA;AACtC,QAAA,iBAAA,CAAkB,IAAI,CAAA;AAAA;AACxB,KACC,GAAA;AAAA,GACF,EAAA;AAAA,IACD,cAAA;AAAA,IACA,iBAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAM,MAAA,eAAA,GAAkB,CAAC,eAA4B,KAAA;AACnD,IAAA,uBAAA,CAAwB,eAAe,CAAA;AACvC,IAAA,oBAAA,CAAqB,IAAI,CAAA;AAAA,GAC3B;AAEA,EAAM,MAAA,wBAAA,GAA2BA,cAAM,CAAA,WAAA,CAAY,MAAM;AACvD,IAAA,CAAC,YAAY;AACX,MAAI,IAAA;AACF,QAAA,MAAM,kBAAmB,CAAA;AAAA,UACvB,eAAiB,EAAA,oBAAA;AAAA,UACjB,eAAiB,EAAA;AAAA,SAClB,CAAA;AACD,QAAA,IAAI,yBAAyB,YAAc,EAAA;AACzC,UAAU,SAAA,EAAA;AACV,UAAkB,iBAAA,EAAA;AAAA;AAEpB,QAAA,oBAAA,CAAqB,KAAK,CAAA;AAAA,eACnB,CAAG,EAAA;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AACd,QAAA,QAAA,CAAS,CAAC,CAAA;AAAA;AACZ,KACC,GAAA;AAAA,GACF,EAAA;AAAA,IACD,kBAAA;AAAA,IACA,iBAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,MAAM,yBAAyBA,cAAM,CAAA,WAAA;AAAA,IACnC,CAAC,mBAA8C,MAAA;AAAA,MAC7C,SACE,kBAAAA,cAAA,CAAA,aAAA;AAAA,QAAC,YAAA;AAAA,QAAA;AAAA,UACC,YAAY,CAAC,eAAA;AAAA,UACb,OAAS,EAAA,MAAM,eAAgB,CAAA,mBAAA,CAAoB,eAAe;AAAA,SAAA;AAAA,QACnE;AAAA;AAED,KAEJ,CAAA;AAAA,IACA,CAAC,eAAe;AAAA,GAClB;AACA,EAAA,MAAM,mBAAsB,GAAA,qBAAA;AAAA,IAC1B,aAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,sBAAsBA,cAAM,CAAA,WAAA;AAAA,IAChC,CAAC,WAAwB,KAAA;AACvB,MAAA,MAAM,qBAAwB,GAAA,MAAA,CAAO,OAAQ,CAAA,mBAAmB,CAAE,CAAA,MAAA;AAAA,QAChE,CAAC,GAAA,EAAK,CAAC,GAAA,EAAK,KAAK,CAAM,KAAA;AACrB,UAAA,MAAM,gBAAgB,KAAM,CAAA,MAAA;AAAA,YAAO,CAAA,IAAA,KACjC,IAAK,CAAA,IAAA,CACF,iBAAkB,CAAA,OAAO,EACzB,QAAS,CAAA,WAAA,CAAY,iBAAkB,CAAA,OAAO,CAAC;AAAA,WACpD;AACA,UAAI,IAAA,aAAA,CAAc,SAAS,CAAG,EAAA;AAC5B,YAAA,GAAA,CAAI,GAAG,CAAI,GAAA,aAAA;AAAA;AAEb,UAAO,OAAA,GAAA;AAAA,SACT;AAAA,QACA;AAAC,OACH;AACA,MAAO,OAAA,qBAAA;AAAA,KACT;AAAA,IACA,CAAC,mBAAmB;AAAA,GACtB;AAEA,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAA,WAAA,CAAY,oBAAoB,CAAA;AAAA,GAClC,EAAG,CAAC,oBAAoB,CAAC,CAAA;AAEzB,EAAA,MAAM,qBAAqBA,cAAM,CAAA,WAAA;AAAA,IAC/B,CACE,GACA,YACG,KAAA;AACH,MAAA,iBAAA,CAAkB,KAAK,CAAA;AACvB,MAAA,iBAAA,CAAkB,CAAC,IAAiB,KAAA;AAClC,QAAA,IAAI,SAAS,YAAc,EAAA;AACzB,UAAA,OAAO,OAAO,YAAY,CAAA;AAAA;AAE5B,QAAO,OAAA,IAAA;AAAA,OACR,CAAA;AACD,MAAA,eAAA,CAAgB,EAAE,CAAA;AAClB,MAAe,cAAA,CAAA,EAAE,OAAS,EAAA,IAAA,EAAM,CAAA;AAChC,MAAA,iBAAA,CAAkB,SAAS,cAAe,EAAA;AAAA,KAC5C;AAAA,IACA,CAAC,iBAAA,EAAmB,cAAgB,EAAA,eAAA,EAAiB,iBAAiB;AAAA,GACxE;AAEA,EAAM,MAAA,iBAAA,GAAoB,CAAC,CAAC,aAAc,CAAA,IAAA;AAAA,IACxC,CAAC,CAA2B,KAAA,CAAA,CAAE,eAAoB,KAAA;AAAA,GACpD;AAEA,EAAA,MAAM,cACH,GAAA,cAAA,IAAkB,oBAAqB,CAAA,MAAA,KAAW,CAClD,IAAA,CAAC,iBAAqB,IAAA,oBAAA,CAAqB,MAAW,KAAA,CAAA,GACnD,aAAe,EAAA,GAAA,CAAI,CAAAC,OAAW,MAAA;AAAA,IAC5B,OAAOA,OAAO,CAAA,KAAA;AAAA,IACd,SAASA,OAAO,CAAA,OAAA;AAAA,IAChB,SAAS,MAAM;AACb,MAAA,WAAA,CAAYA,QAAO,OAAO,CAAA;AAAA;AAC5B,GACF,CAAE,IACF,EAAC;AAEP,EAAA,MAAM,YAAe,GAAAD,cAAA,CAAM,WAAY,CAAA,CAAC,KAAkB,KAAA;AACxD,IAAA,cAAA,CAAe,KAAK,CAAA;AAAA,GACtB,EAAG,EAAE,CAAA;AAEL,EAAM,MAAA,cAAA,GAAiBA,cAAM,CAAA,WAAA,CAAY,MAAM;AAC7C,IAAgB,eAAA,CAAA,CAAA,MAAA,KAAU,CAAC,MAAM,CAAA;AAAA,GACnC,EAAG,EAAE,CAAA;AAEL,EAAM,MAAA,YAAA,GAAe,CAAC,IAAA,EAAc,KAAqB,KAAA;AACvD,IAAA,KAAA,CAAM,cAAe,EAAA;AACrB,IAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,GACvB;AAEA,EAAM,MAAA,gBAAA,GAAmB,CAAC,IAA0B,KAAA;AAClD,IAAA,IAAA,CAAK,QAAQ,CAAc,UAAA,KAAA;AACzB,MAAI,IAAA,CAAC,CAAC,UAAW,CAAA,MAAA,CAAO,KAAK,CAAK,CAAA,KAAA,CAAA,CAAE,IAAS,KAAA,mBAAmB,CAAG,EAAA;AACjE,QAAA,YAAA,CAAa,IAAI,CAAA;AACjB,QAAe,cAAA,CAAA;AAAA,UACb,OACE,EAAA;AAAA,SACH,CAAA;AAAA;AACH,KACD,CAAA;AAAA,GACH;AAEA,EAAA,IAAI,KAAO,EAAA;AACT,IAAA,oDACG,GAAI,EAAA,EAAA,OAAA,EAAS,qBACXA,cAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAc,CAC5B,CAAA;AAAA;AAIJ,EAAA,mFAEK,iBACC,oBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,MAAQ,EAAA,iBAAA;AAAA,MACR,OAAA,EAAS,MAAM,oBAAA,CAAqB,KAAK,CAAA;AAAA,MACzC,SAAW,EAAA;AAAA;AAAA,GAGf,kBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACC,aAAa,kBAAmB,CAAA,QAAA;AAAA,MAChC,WAAW,OAAQ,CAAA;AAAA,KAAA;AAAA,iDAElB,aAAc,EAAA,EAAA,SAAA,EAAW,OAAQ,CAAA,MAAA,EAAA,+CAC/B,iBACC,EAAA,IAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,MAAC,iBAAA;AAAA,MAAA;AAAA,QACC,eAAe,EAAA,YAAA;AAAA,QACf,YAAc,EAAA,MAAM,eAAgB,CAAA,CAAC,YAAY,CAAA;AAAA,QACjD,WAAW,OAAQ,CAAA;AAAA;AAAA,KAErB,kBAAAA,cAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,SAAA,EAAW,QAAQ,WACrC,EAAA,kBAAAA,cAAA,CAAA,aAAA,CAAC,KAAM,EAAA,EAAA,YAAA,EAAa,MAAK,IAAK,EAAA,KAAA,EAAA,EAAM,sBAEpC,CACF,CACF,CAEA,kBAAAA,cAAA,CAAA,aAAA;AAAA,MAAC,uBAAA;AAAA,MAAA;AAAA,QACC,aAAA;AAAA,QACA,mBAAA,EAAqB,CAAQ,IAAA,KAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,QACrD;AAAA;AAAA,KAEJ,CAAA;AAAA,oBACAA,cAAA,CAAA,aAAA;AAAA,MAAC,6BAAA;AAAA,MAAA;AAAA,QACC,uBAAyB,EAAA,EAAE,WAAa,EAAA,IAAA,EAAM,SAAS,OAAQ,EAAA;AAAA,QAC/D,kBAAkB,EAAA,IAAA;AAAA,QAClB,aAAa,kBAAmB,CAAA,QAAA;AAAA,QAChC,cAAA;AAAA,QACA,YAAA;AAAA,QACA,eAAA;AAAA,QACA,YAAc,EAAA,cAAA;AAAA,QACd,kBAAA;AAAA,QACA,aAAA,EAAe,oBAAoB,WAAW,CAAA;AAAA,QAC9C,SAAA,EAAW,iBAAiB,SAAY,GAAA,SAAA;AAAA,QACxC,qBAAuB,EAAA,YAAA;AAAA,QACvB,sBAAuB,EAAA,0BAAA;AAAA,QACvB,aACE,kBAAAA,cAAA,CAAA,aAAA;AAAA,UAAC,YAAA;AAAA,UAAA;AAAA,YACC,YAAY,CAAC,CAAA,EAAG,IAAS,KAAA,YAAA,CAAa,MAAM,CAAC,CAAA;AAAA,YAC7C,aAAa,kBAAmB,CAAA,QAAA;AAAA,YAChC,QAAS,EAAA,wFAAA;AAAA,YACT,gBAAkB,EAAA,kBAAA;AAAA,YAClB;AAAA,WAAA;AAAA,UAEC,aAAa,WAAY,CAAA,OAAA,iDACvB,KAAI,EAAA,EAAA,SAAA,EAAW,QAAQ,cACtB,EAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,YAAA;AAAA,YAAA;AAAA,cACC,SAAU,EAAA,IAAA;AAAA,cACV,KAAM,EAAA,oBAAA;AAAA,cACN,OAAA,EAAS,YAAY,IAAQ,IAAA,QAAA;AAAA,cAC7B,QAAQ,EAAA,IAAA;AAAA,cACR,SAAS,MAAM,cAAA,CAAe,EAAE,OAAA,EAAS,MAAM;AAAA,aAAA;AAAA,YAE9C,WAAY,CAAA;AAAA,WAEjB,CAAA;AAAA,uDAGD,cACC,EAAA,IAAA,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,iBAAA;AAAA,YAAA;AAAA,cACC,QAAA;AAAA,cACA,QAAA;AAAA,cACA,cAAA;AAAA,cACA,YAAA;AAAA,cACA,GAAK,EAAA,iBAAA;AAAA,cACL,cAAA;AAAA,cACA,cAAA;AAAA,cACA,WAAa,EAAA;AAAA;AAAA,WAEjB,CAAA;AAAA,uDACC,aAAc,EAAA,EAAA,SAAA,EAAW,QAAQ,MAChC,EAAA,kBAAAA,cAAA,CAAA,aAAA,CAAC,iBAAY,CACb,kBAAAA,cAAA,CAAA,aAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,aAAe,EAAA,WAAA;AAAA,cACf,oBAAA;AAAA,cACA,eAAe,EAAA,IAAA;AAAA,cACf,YAAA;AAAA,cACA,mBAAmB,EAAA,IAAA;AAAA,cACnB,WAAa,EAAA;AAAA,gBACX,MAAQ,EAAA;AAAA,kBACN,WAAa,EAAA;AAAA;AACf,eACF;AAAA,cACA,gBAAkB,EAAA,kBAAA;AAAA,cAClB,gBAAA;AAAA,cACA,WAAY,EAAA;AAAA;AAAA,WACd,+CACC,eAAiB,EAAA,EAAA,GAAG,iBAAiB,OAAQ,CAAA,aAAa,GAAG,CAChE;AAAA;AACF;AAAA;AAEJ,GACF,kBACCA,cAAA,CAAA,aAAA,CAAA,UAAA,EAAA,IAAW,CACd,CAAA;AAEJ;;;;"}
|
|
@@ -6,6 +6,7 @@ import { Alert } from '@patternfly/react-core';
|
|
|
6
6
|
import { FUNCTION_DISCLAIMER, FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION } from '../const.esm.js';
|
|
7
7
|
import { useAutoScroll } from '../hooks/useAutoScroll.esm.js';
|
|
8
8
|
import { useBufferedMessages } from '../hooks/useBufferedMessages.esm.js';
|
|
9
|
+
import { useFeedbackActions } from '../hooks/useFeedbackActions.esm.js';
|
|
9
10
|
|
|
10
11
|
const useStyles = makeStyles((theme) => ({
|
|
11
12
|
prompt: {
|
|
@@ -32,8 +33,10 @@ const LightspeedChatBox = React__default.forwardRef(
|
|
|
32
33
|
userName,
|
|
33
34
|
messages,
|
|
34
35
|
announcement,
|
|
36
|
+
conversationId,
|
|
35
37
|
profileLoading,
|
|
36
|
-
welcomePrompts
|
|
38
|
+
welcomePrompts,
|
|
39
|
+
isStreaming
|
|
37
40
|
}, ref) => {
|
|
38
41
|
const classes = useStyles();
|
|
39
42
|
const scrollQueued = React__default.useRef(false);
|
|
@@ -42,6 +45,11 @@ const LightspeedChatBox = React__default.forwardRef(
|
|
|
42
45
|
const questionValidationEnabled = configApi.getOptionalBoolean("lightspeed.questionValidation") ?? true;
|
|
43
46
|
const cmessages = useBufferedMessages(messages, 30);
|
|
44
47
|
const { autoScroll, scrollToBottom, scrollToTop } = useAutoScroll(containerRef);
|
|
48
|
+
const conversationMessages = useFeedbackActions(
|
|
49
|
+
cmessages,
|
|
50
|
+
conversationId,
|
|
51
|
+
isStreaming
|
|
52
|
+
);
|
|
45
53
|
React__default.useImperativeHandle(ref, () => ({
|
|
46
54
|
scrollToBottom: () => {
|
|
47
55
|
if (scrollQueued.current) return;
|
|
@@ -99,7 +107,7 @@ const LightspeedChatBox = React__default.forwardRef(
|
|
|
99
107
|
prompts: welcomePrompts
|
|
100
108
|
}
|
|
101
109
|
) : /* @__PURE__ */ React__default.createElement("br", null),
|
|
102
|
-
|
|
110
|
+
conversationMessages.map((message, index) => {
|
|
103
111
|
if (index === cmessages.length - 1) {
|
|
104
112
|
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, { key: `${message.role}-${index}` }, /* @__PURE__ */ React__default.createElement(Message, { key: `${message.role}-${index}`, ...message }));
|
|
105
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LightspeedChatBox.esm.js","sources":["../../src/components/LightspeedChatBox.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 React from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { makeStyles } from '@material-ui/core';\nimport {\n ChatbotWelcomePrompt,\n Message,\n MessageBox,\n MessageBoxHandle,\n MessageProps,\n WelcomePrompt,\n} from '@patternfly/chatbot';\nimport { Alert } from '@patternfly/react-core';\n\nimport {\n FUNCTION_DISCLAIMER,\n FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION,\n} from '../const';\nimport { useAutoScroll } from '../hooks/useAutoScroll';\nimport { useBufferedMessages } from '../hooks/useBufferedMessages';\n\nconst useStyles = makeStyles(theme => ({\n prompt: {\n 'justify-content': 'flex-end',\n },\n container: {\n maxWidth: 'unset !important',\n },\n alert: {\n background: 'unset !important',\n },\n userMessageText: {\n '& div.pf-chatbot__message--user': {\n '& div.pf-chatbot__message-text': {\n '& p': {\n color: theme.palette.common.white,\n },\n },\n },\n },\n}));\n\ntype LightspeedChatBoxProps = {\n userName?: string;\n messages: MessageProps[];\n profileLoading: boolean;\n announcement: string | undefined;\n welcomePrompts: WelcomePrompt[];\n};\n\nexport interface ScrollContainerHandle {\n scrollToBottom: () => void;\n}\n\nexport const LightspeedChatBox = React.forwardRef(\n (\n {\n userName,\n messages,\n announcement,\n profileLoading,\n welcomePrompts,\n }: LightspeedChatBoxProps,\n ref: React.ForwardedRef<ScrollContainerHandle>,\n ) => {\n const classes = useStyles();\n const scrollQueued = React.useRef(false);\n const containerRef = React.useRef<MessageBoxHandle>(null);\n\n const configApi = useApi(configApiRef);\n const questionValidationEnabled =\n configApi.getOptionalBoolean('lightspeed.questionValidation') ?? true;\n const cmessages = useBufferedMessages(messages, 30);\n const { autoScroll, scrollToBottom, scrollToTop } =\n useAutoScroll(containerRef);\n\n React.useImperativeHandle(ref, () => ({\n scrollToBottom: () => {\n if (scrollQueued.current) return;\n scrollQueued.current = true;\n\n requestAnimationFrame(() => {\n scrollToBottom();\n scrollQueued.current = false;\n });\n },\n }));\n\n // Auto-scrolls to the latest message\n React.useEffect(() => {\n if (!autoScroll || scrollQueued.current) return undefined;\n\n scrollQueued.current = true;\n\n const rafId = requestAnimationFrame(() => {\n const container = containerRef.current;\n if (!container) return;\n\n setTimeout(() => {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'auto',\n });\n }, 0);\n\n scrollQueued.current = false;\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n scrollQueued.current = false;\n };\n\n // eslint-disable-next-line\n }, [autoScroll, cmessages, containerRef]);\n\n const messageBoxClasses = `${classes.container} ${classes.userMessageText}`;\n return (\n <MessageBox\n className={\n welcomePrompts.length\n ? `${messageBoxClasses} ${classes.prompt}`\n : messageBoxClasses\n }\n announcement={announcement}\n ref={containerRef}\n onScrollToTopClick={scrollToTop}\n onScrollToBottomClick={scrollToBottom}\n >\n <div>\n <Alert\n title=\"Important\"\n variant=\"info\"\n isInline\n className={classes.alert}\n >\n {questionValidationEnabled\n ? FUNCTION_DISCLAIMER\n : FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION}\n </Alert>\n <br />\n </div>\n {welcomePrompts.length ? (\n <ChatbotWelcomePrompt\n title={`Hello, ${profileLoading ? '...' : (userName ?? 'Guest')}`}\n description=\"How can I help you today?\"\n prompts={welcomePrompts}\n />\n ) : (\n <br />\n )}\n {
|
|
1
|
+
{"version":3,"file":"LightspeedChatBox.esm.js","sources":["../../src/components/LightspeedChatBox.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 React from 'react';\n\nimport { configApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { makeStyles } from '@material-ui/core';\nimport {\n ChatbotWelcomePrompt,\n Message,\n MessageBox,\n MessageBoxHandle,\n MessageProps,\n WelcomePrompt,\n} from '@patternfly/chatbot';\nimport { Alert } from '@patternfly/react-core';\n\nimport {\n FUNCTION_DISCLAIMER,\n FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION,\n} from '../const';\nimport { useAutoScroll } from '../hooks/useAutoScroll';\nimport { useBufferedMessages } from '../hooks/useBufferedMessages';\nimport { useFeedbackActions } from '../hooks/useFeedbackActions';\n\nconst useStyles = makeStyles(theme => ({\n prompt: {\n 'justify-content': 'flex-end',\n },\n container: {\n maxWidth: 'unset !important',\n },\n alert: {\n background: 'unset !important',\n },\n userMessageText: {\n '& div.pf-chatbot__message--user': {\n '& div.pf-chatbot__message-text': {\n '& p': {\n color: theme.palette.common.white,\n },\n },\n },\n },\n}));\n\ntype LightspeedChatBoxProps = {\n userName?: string;\n messages: MessageProps[];\n profileLoading: boolean;\n announcement: string | undefined;\n welcomePrompts: WelcomePrompt[];\n conversationId: string;\n isStreaming: boolean;\n};\n\nexport interface ScrollContainerHandle {\n scrollToBottom: () => void;\n}\n\nexport const LightspeedChatBox = React.forwardRef(\n (\n {\n userName,\n messages,\n announcement,\n conversationId,\n profileLoading,\n welcomePrompts,\n isStreaming,\n }: LightspeedChatBoxProps,\n ref: React.ForwardedRef<ScrollContainerHandle>,\n ) => {\n const classes = useStyles();\n const scrollQueued = React.useRef(false);\n const containerRef = React.useRef<MessageBoxHandle>(null);\n\n const configApi = useApi(configApiRef);\n const questionValidationEnabled =\n configApi.getOptionalBoolean('lightspeed.questionValidation') ?? true;\n const cmessages = useBufferedMessages(messages, 30);\n const { autoScroll, scrollToBottom, scrollToTop } =\n useAutoScroll(containerRef);\n const conversationMessages = useFeedbackActions(\n cmessages,\n conversationId,\n isStreaming,\n );\n\n React.useImperativeHandle(ref, () => ({\n scrollToBottom: () => {\n if (scrollQueued.current) return;\n scrollQueued.current = true;\n\n requestAnimationFrame(() => {\n scrollToBottom();\n scrollQueued.current = false;\n });\n },\n }));\n\n // Auto-scrolls to the latest message\n React.useEffect(() => {\n if (!autoScroll || scrollQueued.current) return undefined;\n\n scrollQueued.current = true;\n\n const rafId = requestAnimationFrame(() => {\n const container = containerRef.current;\n if (!container) return;\n\n setTimeout(() => {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'auto',\n });\n }, 0);\n\n scrollQueued.current = false;\n });\n\n return () => {\n cancelAnimationFrame(rafId);\n scrollQueued.current = false;\n };\n\n // eslint-disable-next-line\n }, [autoScroll, cmessages, containerRef]);\n\n const messageBoxClasses = `${classes.container} ${classes.userMessageText}`;\n return (\n <MessageBox\n className={\n welcomePrompts.length\n ? `${messageBoxClasses} ${classes.prompt}`\n : messageBoxClasses\n }\n announcement={announcement}\n ref={containerRef}\n onScrollToTopClick={scrollToTop}\n onScrollToBottomClick={scrollToBottom}\n >\n <div>\n <Alert\n title=\"Important\"\n variant=\"info\"\n isInline\n className={classes.alert}\n >\n {questionValidationEnabled\n ? FUNCTION_DISCLAIMER\n : FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION}\n </Alert>\n <br />\n </div>\n {welcomePrompts.length ? (\n <ChatbotWelcomePrompt\n title={`Hello, ${profileLoading ? '...' : (userName ?? 'Guest')}`}\n description=\"How can I help you today?\"\n prompts={welcomePrompts}\n />\n ) : (\n <br />\n )}\n {conversationMessages.map((message, index) => {\n if (index === cmessages.length - 1) {\n return (\n <React.Fragment key={`${message.role}-${index}`}>\n <Message key={`${message.role}-${index}`} {...message} />\n </React.Fragment>\n );\n }\n return <Message key={`${message.role}-${index}`} {...message} />;\n })}\n </MessageBox>\n );\n },\n);\n"],"names":["React"],"mappings":"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"}
|
package/dist/const.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const TEMP_CONVERSATION_ID = "temp-conversation-id";
|
|
2
|
-
const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
|
|
3
|
-
const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
|
|
2
|
+
const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
|
|
3
|
+
const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Developer Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
|
|
4
4
|
const createPrompt = (title, message) => {
|
|
5
5
|
return { title, message };
|
|
6
6
|
};
|
package/dist/const.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const.esm.js","sources":["../src/const.ts"],"sourcesContent":["import { SamplePrompts } from './types';\n\n/*\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 */\nexport const TEMP_CONVERSATION_ID = 'temp-conversation-id';\n\nexport const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nexport const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nconst createPrompt = (title: string, message: string) => {\n return { title, message };\n};\n\nexport const supportedFileTypes = {\n 'text/plain': ['.txt'],\n 'application/json': ['.json'],\n 'application/yaml': ['.yaml', '.yml'],\n 'application/xml': ['.xml'],\n};\n\nexport const DEFAULT_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Get Help On Code Readability',\n 'Can you suggest techniques I can use to make my code more readable and maintainable?',\n ),\n createPrompt(\n 'Get Help With Debugging',\n 'My application is throwing an error when trying to connect to the database. Can you help me identify the issue?',\n ),\n createPrompt(\n 'Explain a Development Concept',\n 'Can you explain how microservices architecture works and its advantages over a monolithic design?',\n ),\n createPrompt(\n 'Suggest Code Optimizations',\n 'Can you suggest common ways to optimize code to achieve better performance?',\n ),\n createPrompt(\n 'Documentation Summary',\n 'Can you summarize the documentation for implementing OAuth 2.0 authentication in a web app?',\n ),\n createPrompt(\n 'Workflows With Git',\n 'I want to make changes to code on another branch without loosing my existing work. What is the procedure to do this using Git?',\n ),\n createPrompt(\n 'Suggest Testing Strategies',\n 'Can you recommend some common testing strategies that will make my application robust and error-free?',\n ),\n createPrompt(\n 'Demystify Sorting Algorithms',\n 'Can you explain the difference between a quicksort and a merge sort algorithm, and when to use each?',\n ),\n createPrompt(\n 'Understand Event-Driven Architecture',\n 'Can you explain what event-driven architecture is and when it’s beneficial to use it in software development?',\n ),\n];\n\nexport const RHDH_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Deploy With Tekton',\n 'Can you help me automate the deployment of my application using Tekton pipelines?',\n ),\n createPrompt(\n 'Create An OpenShift Deployment',\n 'Can you guide me through creating a new deployment in OpenShift for a containerized application?',\n ),\n createPrompt(\n 'Getting Started with Red Hat Developer Hub',\n 'Can you guide me through the first steps to start using Developer Hub as a developer, like exploring the Software Catalog and adding my service?',\n ),\n];\n"],"names":[],"mappings":"AAiBO,MAAM,oBAAuB,GAAA;AAE7B,MAAM,+CAAkD,GAAA,CAAA,
|
|
1
|
+
{"version":3,"file":"const.esm.js","sources":["../src/const.ts"],"sourcesContent":["import { SamplePrompts } from './types';\n\n/*\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 */\nexport const TEMP_CONVERSATION_ID = 'temp-conversation-id';\n\nexport const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nexport const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Developer Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nconst createPrompt = (title: string, message: string) => {\n return { title, message };\n};\n\nexport const supportedFileTypes = {\n 'text/plain': ['.txt'],\n 'application/json': ['.json'],\n 'application/yaml': ['.yaml', '.yml'],\n 'application/xml': ['.xml'],\n};\n\nexport const DEFAULT_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Get Help On Code Readability',\n 'Can you suggest techniques I can use to make my code more readable and maintainable?',\n ),\n createPrompt(\n 'Get Help With Debugging',\n 'My application is throwing an error when trying to connect to the database. Can you help me identify the issue?',\n ),\n createPrompt(\n 'Explain a Development Concept',\n 'Can you explain how microservices architecture works and its advantages over a monolithic design?',\n ),\n createPrompt(\n 'Suggest Code Optimizations',\n 'Can you suggest common ways to optimize code to achieve better performance?',\n ),\n createPrompt(\n 'Documentation Summary',\n 'Can you summarize the documentation for implementing OAuth 2.0 authentication in a web app?',\n ),\n createPrompt(\n 'Workflows With Git',\n 'I want to make changes to code on another branch without loosing my existing work. What is the procedure to do this using Git?',\n ),\n createPrompt(\n 'Suggest Testing Strategies',\n 'Can you recommend some common testing strategies that will make my application robust and error-free?',\n ),\n createPrompt(\n 'Demystify Sorting Algorithms',\n 'Can you explain the difference between a quicksort and a merge sort algorithm, and when to use each?',\n ),\n createPrompt(\n 'Understand Event-Driven Architecture',\n 'Can you explain what event-driven architecture is and when it’s beneficial to use it in software development?',\n ),\n];\n\nexport const RHDH_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Deploy With Tekton',\n 'Can you help me automate the deployment of my application using Tekton pipelines?',\n ),\n createPrompt(\n 'Create An OpenShift Deployment',\n 'Can you guide me through creating a new deployment in OpenShift for a containerized application?',\n ),\n createPrompt(\n 'Getting Started with Red Hat Developer Hub',\n 'Can you guide me through the first steps to start using Developer Hub as a developer, like exploring the Software Catalog and adding my service?',\n ),\n];\n"],"names":[],"mappings":"AAiBO,MAAM,oBAAuB,GAAA;AAE7B,MAAM,+CAAkD,GAAA,CAAA,sbAAA;AAExD,MAAM,mBAAsB,GAAA,CAAA,8nBAAA;AAEnC,MAAM,YAAA,GAAe,CAAC,KAAA,EAAe,OAAoB,KAAA;AACvD,EAAO,OAAA,EAAE,OAAO,OAAQ,EAAA;AAC1B,CAAA;AAEO,MAAM,kBAAqB,GAAA;AAAA,EAChC,YAAA,EAAc,CAAC,MAAM,CAAA;AAAA,EACrB,kBAAA,EAAoB,CAAC,OAAO,CAAA;AAAA,EAC5B,kBAAA,EAAoB,CAAC,OAAA,EAAS,MAAM,CAAA;AAAA,EACpC,iBAAA,EAAmB,CAAC,MAAM;AAC5B;AAEO,MAAM,sBAAwC,GAAA;AAAA,EACnD,YAAA;AAAA,IACE,8BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,yBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,+BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,uBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,oBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,8BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,sCAAA;AAAA,IACA;AAAA;AAEJ;AAEO,MAAM,mBAAqC,GAAA;AAAA,EAChD,YAAA;AAAA,IACE,oBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,gCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4CAAA;AAAA,IACA;AAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
2
|
+
import { useMutation } from '@tanstack/react-query';
|
|
3
|
+
import { lightspeedApiRef } from '../api/api.esm.js';
|
|
4
|
+
|
|
5
|
+
const useCaptureFeedback = () => {
|
|
6
|
+
const lightspeedApi = useApi(lightspeedApiRef);
|
|
7
|
+
return useMutation({
|
|
8
|
+
mutationFn: async (payload) => {
|
|
9
|
+
if (!payload.conversation_id) {
|
|
10
|
+
throw new Error("Failed to capture the user Feedback");
|
|
11
|
+
}
|
|
12
|
+
return await lightspeedApi.captureFeedback(payload);
|
|
13
|
+
},
|
|
14
|
+
onError: (error) => {
|
|
15
|
+
console.warn(error);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { useCaptureFeedback };
|
|
21
|
+
//# sourceMappingURL=useCaptureFeedback.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCaptureFeedback.esm.js","sources":["../../src/hooks/useCaptureFeedback.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { useMutation } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { CaptureFeedback } from '../types';\n\nexport const useCaptureFeedback = () => {\n const lightspeedApi = useApi(lightspeedApiRef);\n\n return useMutation({\n mutationFn: async (payload: CaptureFeedback) => {\n if (!payload.conversation_id) {\n throw new Error('Failed to capture the user Feedback');\n }\n\n return await lightspeedApi.captureFeedback(payload);\n },\n onError: error => {\n // eslint-disable-next-line\n console.warn(error);\n },\n });\n};\n"],"names":[],"mappings":";;;;AAsBO,MAAM,qBAAqB,MAAM;AACtC,EAAM,MAAA,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAE7C,EAAA,OAAO,WAAY,CAAA;AAAA,IACjB,UAAA,EAAY,OAAO,OAA6B,KAAA;AAC9C,MAAI,IAAA,CAAC,QAAQ,eAAiB,EAAA;AAC5B,QAAM,MAAA,IAAI,MAAM,qCAAqC,CAAA;AAAA;AAGvD,MAAO,OAAA,MAAM,aAAc,CAAA,eAAA,CAAgB,OAAO,CAAA;AAAA,KACpD;AAAA,IACA,SAAS,CAAS,KAAA,KAAA;AAEhB,MAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA;AACpB,GACD,CAAA;AACH;;;;"}
|
|
@@ -196,12 +196,11 @@ const useConversationMessages = (conversationId, userName, selectedModel, avatar
|
|
|
196
196
|
}) : { ...conversation[lastMessageIndex] };
|
|
197
197
|
if (documents.length) {
|
|
198
198
|
lastMessage.sources = {
|
|
199
|
-
sources: documents.map(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
)
|
|
199
|
+
sources: documents.map((doc) => ({
|
|
200
|
+
title: doc.doc_title,
|
|
201
|
+
link: doc.doc_url,
|
|
202
|
+
body: doc.doc_description
|
|
203
|
+
}))
|
|
205
204
|
};
|
|
206
205
|
}
|
|
207
206
|
const updatedConversation = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationMessages.esm.js","sources":["../../src/hooks/useConversationMessages.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 React from 'react';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport { useQuery } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ScrollContainerHandle } from '../components/LightspeedChatBox';\nimport { TEMP_CONVERSATION_ID } from '../const';\nimport botAvatar from '../images/bot-avatar.svg';\nimport userAvatar from '../images/user-avatar.svg';\nimport { Attachment } from '../types';\nimport {\n createBotMessage,\n createUserMessage,\n getMessageData,\n getTimestamp,\n transformDocumentsToSources,\n} from '../utils/lightspeed-chatbox-utils';\nimport { useCreateConversationMessage } from './useCreateCoversationMessage';\n\n// Fetch all conversation messages\nexport const useFetchConversationMessages = (currentConversation: string) => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['conversationMessages', currentConversation],\n queryFn: currentConversation\n ? async () => {\n const response =\n await lightspeedApi.getConversationMessages(currentConversation);\n\n return response;\n }\n : undefined,\n retry: false,\n });\n};\n\ntype Conversations = { [_key: string]: MessageProps[] };\n\n/**\n * Fetches all the messages for given conversation_id\n * @param conversationId\n * @param userName\n * @param selectedModel\n * @param avatar\n *\n */\nexport const useConversationMessages = (\n conversationId: string,\n userName: string | undefined,\n selectedModel: string,\n avatar: string = userAvatar,\n onComplete?: (message: string) => void,\n onStart?: (conversation_id: string) => void,\n) => {\n const { mutateAsync: createMessage } = useCreateConversationMessage();\n const scrollToBottomRef = React.useRef<ScrollContainerHandle>(null);\n\n const [currentConversation, setCurrentConversation] =\n React.useState(conversationId);\n const [conversations, setConversations] = React.useState<Conversations>({\n [currentConversation]: [],\n });\n const streamingConversations = React.useRef<Conversations>({\n [currentConversation]: [],\n });\n\n React.useEffect(() => {\n if (currentConversation !== conversationId) {\n setCurrentConversation(conversationId);\n setConversations(prev => {\n if (prev[conversationId]) return prev;\n\n return {\n ...prev,\n [conversationId]: [],\n };\n });\n }\n }, [currentConversation, conversationId]);\n\n const { data: conversationsData = [], ...queryProps } =\n useFetchConversationMessages(currentConversation);\n\n React.useEffect(() => {\n if (\n !Array.isArray(conversationsData) ||\n (conversationsData.length === 0 &&\n conversationId !== TEMP_CONVERSATION_ID)\n )\n return;\n\n const newConvoIndex: number[] = [];\n\n if (conversations) {\n const _conversations: { [key: string]: any[] } = {\n [currentConversation]: [],\n };\n\n let index = 0;\n for (let i = 0; i < conversationsData.length; i += 2) {\n const userMessage = conversationsData[i];\n const aiMessage = conversationsData[i + 1];\n\n const { content: humanMessage, timestamp: userTimestamp } =\n getMessageData(userMessage);\n const {\n model,\n content: botMessage,\n timestamp: botTimestamp,\n referencedDocuments,\n } = getMessageData(aiMessage);\n\n _conversations[currentConversation].push(\n ...[\n createUserMessage({\n avatar,\n name: userName,\n content: humanMessage,\n timestamp: userTimestamp,\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: false,\n name: model ?? selectedModel,\n content: botMessage,\n timestamp: botTimestamp,\n sources: transformDocumentsToSources(referencedDocuments),\n }),\n ],\n );\n\n newConvoIndex.push(index);\n index++;\n }\n\n if (streamingConversations.current[currentConversation]) {\n _conversations[currentConversation].push(\n ...streamingConversations.current[currentConversation],\n );\n }\n\n setConversations(_conversations);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n conversationsData,\n userName,\n avatar,\n currentConversation,\n selectedModel,\n streamingConversations,\n ]);\n\n const handleInputPrompt = React.useCallback(\n async (prompt: string, attachments: Attachment[] = []) => {\n let newConversationId = '';\n\n const conversationTuple = [\n createUserMessage({\n avatar,\n name: userName,\n content: prompt,\n timestamp: getTimestamp(Date.now()) ?? '',\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: true,\n name: selectedModel,\n content: '',\n timestamp: '',\n }),\n ];\n\n streamingConversations.current = {\n ...streamingConversations.current,\n [currentConversation]: conversationTuple,\n };\n\n setConversations((prevConv: Conversations) => {\n return {\n ...prevConv,\n [currentConversation]: [\n ...(prevConv?.[currentConversation] ?? []),\n ...conversationTuple,\n ],\n };\n });\n\n setTimeout(() => {\n scrollToBottomRef.current?.scrollToBottom();\n }, 0);\n const finalMessages: string[] = [];\n let buffer = '';\n\n try {\n const reader = await createMessage({\n prompt,\n selectedModel,\n currentConversation,\n attachments,\n });\n\n const decoder = new TextDecoder('utf-8');\n const keepGoing = true;\n\n while (keepGoing) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process all complete messages separated by double newlines\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const lines = part\n .split('\\n')\n .filter(line => line.startsWith('data:'));\n\n const jsonString = lines\n .map(line => line.trim().slice(5).trim())\n .join('');\n try {\n const { event, data } = JSON.parse(jsonString);\n if (event === 'start') {\n if (currentConversation === TEMP_CONVERSATION_ID) {\n // If the conversation is temp, we need to set the new conversation id\n newConversationId = data?.conversation_id;\n }\n }\n\n if (event === 'token') {\n const content = data?.token || '';\n\n finalMessages.push(content);\n\n // Store streaming message\n const [humanMessage, aiMessage] =\n streamingConversations.current[currentConversation];\n streamingConversations.current[currentConversation] = [\n humanMessage,\n { ...aiMessage, content: aiMessage.content + content },\n ];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += content;\n lastMessage.name =\n data?.response_metadata?.model || selectedModel;\n lastMessage.timestamp = getTimestamp(\n data?.response_metadata?.created_at || Date.now(),\n );\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n\n if (event === 'end') {\n const documents = data?.referenced_documents || [];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n if (documents.length) {\n lastMessage.sources = {\n sources: documents.map(\n (doc: { doc_title: string; doc_url: string }) => ({\n title: doc.doc_title,\n link: doc.doc_url,\n }),\n ),\n };\n }\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn('Error parsing JSON:', error);\n if (typeof onComplete === 'function') {\n onComplete('Invalid JSON received');\n }\n }\n }\n }\n } catch (e) {\n setConversations(prevConversations => {\n const conversation = prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += e;\n lastMessage.error = {\n title: e.message,\n };\n lastMessage.timestamp = getTimestamp(Date.now());\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n finalMessages.push(`${e}`);\n\n return {\n ...prevConversations,\n [newConversationId.length > 0\n ? newConversationId\n : currentConversation]: updatedConversation,\n };\n });\n }\n // reset current streaming\n streamingConversations.current[currentConversation] = [];\n if (typeof onComplete === 'function') {\n onComplete(finalMessages.join(''));\n }\n // Swap temp conversation messages with new conversation\n\n if (currentConversation === TEMP_CONVERSATION_ID && newConversationId) {\n setConversations(prevConversations => {\n return {\n ...prevConversations,\n [newConversationId]: prevConversations[TEMP_CONVERSATION_ID],\n };\n });\n\n onStart?.(newConversationId);\n\n setConversations(prev => {\n const { temp, ...rest } = prev;\n return rest;\n });\n }\n },\n\n [\n avatar,\n userName,\n onComplete,\n onStart,\n selectedModel,\n createMessage,\n currentConversation,\n ],\n );\n\n return {\n conversationMessages: conversations[currentConversation] ?? [],\n handleInputPrompt,\n conversations,\n scrollToBottomRef,\n ...queryProps,\n };\n};\n"],"names":["React"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useConversationMessages.esm.js","sources":["../../src/hooks/useConversationMessages.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 React from 'react';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport { useQuery } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ScrollContainerHandle } from '../components/LightspeedChatBox';\nimport { TEMP_CONVERSATION_ID } from '../const';\nimport botAvatar from '../images/bot-avatar.svg';\nimport userAvatar from '../images/user-avatar.svg';\nimport { Attachment, ReferencedDocument } from '../types';\nimport {\n createBotMessage,\n createUserMessage,\n getMessageData,\n getTimestamp,\n transformDocumentsToSources,\n} from '../utils/lightspeed-chatbox-utils';\nimport { useCreateConversationMessage } from './useCreateCoversationMessage';\n\n// Fetch all conversation messages\nexport const useFetchConversationMessages = (currentConversation: string) => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['conversationMessages', currentConversation],\n queryFn: currentConversation\n ? async () => {\n const response =\n await lightspeedApi.getConversationMessages(currentConversation);\n\n return response;\n }\n : undefined,\n retry: false,\n });\n};\n\ntype Conversations = { [_key: string]: MessageProps[] };\n\n/**\n * Fetches all the messages for given conversation_id\n * @param conversationId\n * @param userName\n * @param selectedModel\n * @param avatar\n *\n */\nexport const useConversationMessages = (\n conversationId: string,\n userName: string | undefined,\n selectedModel: string,\n avatar: string = userAvatar,\n onComplete?: (message: string) => void,\n onStart?: (conversation_id: string) => void,\n) => {\n const { mutateAsync: createMessage } = useCreateConversationMessage();\n const scrollToBottomRef = React.useRef<ScrollContainerHandle>(null);\n\n const [currentConversation, setCurrentConversation] =\n React.useState(conversationId);\n const [conversations, setConversations] = React.useState<Conversations>({\n [currentConversation]: [],\n });\n const streamingConversations = React.useRef<Conversations>({\n [currentConversation]: [],\n });\n\n React.useEffect(() => {\n if (currentConversation !== conversationId) {\n setCurrentConversation(conversationId);\n setConversations(prev => {\n if (prev[conversationId]) return prev;\n\n return {\n ...prev,\n [conversationId]: [],\n };\n });\n }\n }, [currentConversation, conversationId]);\n\n const { data: conversationsData = [], ...queryProps } =\n useFetchConversationMessages(currentConversation);\n\n React.useEffect(() => {\n if (\n !Array.isArray(conversationsData) ||\n (conversationsData.length === 0 &&\n conversationId !== TEMP_CONVERSATION_ID)\n )\n return;\n\n const newConvoIndex: number[] = [];\n\n if (conversations) {\n const _conversations: { [key: string]: any[] } = {\n [currentConversation]: [],\n };\n\n let index = 0;\n for (let i = 0; i < conversationsData.length; i += 2) {\n const userMessage = conversationsData[i];\n const aiMessage = conversationsData[i + 1];\n\n const { content: humanMessage, timestamp: userTimestamp } =\n getMessageData(userMessage);\n const {\n model,\n content: botMessage,\n timestamp: botTimestamp,\n referencedDocuments,\n } = getMessageData(aiMessage);\n\n _conversations[currentConversation].push(\n ...[\n createUserMessage({\n avatar,\n name: userName,\n content: humanMessage,\n timestamp: userTimestamp,\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: false,\n name: model ?? selectedModel,\n content: botMessage,\n timestamp: botTimestamp,\n sources: transformDocumentsToSources(referencedDocuments),\n }),\n ],\n );\n\n newConvoIndex.push(index);\n index++;\n }\n\n if (streamingConversations.current[currentConversation]) {\n _conversations[currentConversation].push(\n ...streamingConversations.current[currentConversation],\n );\n }\n\n setConversations(_conversations);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n conversationsData,\n userName,\n avatar,\n currentConversation,\n selectedModel,\n streamingConversations,\n ]);\n\n const handleInputPrompt = React.useCallback(\n async (prompt: string, attachments: Attachment[] = []) => {\n let newConversationId = '';\n\n const conversationTuple = [\n createUserMessage({\n avatar,\n name: userName,\n content: prompt,\n timestamp: getTimestamp(Date.now()) ?? '',\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: true,\n name: selectedModel,\n content: '',\n timestamp: '',\n }),\n ];\n\n streamingConversations.current = {\n ...streamingConversations.current,\n [currentConversation]: conversationTuple,\n };\n\n setConversations((prevConv: Conversations) => {\n return {\n ...prevConv,\n [currentConversation]: [\n ...(prevConv?.[currentConversation] ?? []),\n ...conversationTuple,\n ],\n };\n });\n\n setTimeout(() => {\n scrollToBottomRef.current?.scrollToBottom();\n }, 0);\n const finalMessages: string[] = [];\n let buffer = '';\n\n try {\n const reader = await createMessage({\n prompt,\n selectedModel,\n currentConversation,\n attachments,\n });\n\n const decoder = new TextDecoder('utf-8');\n const keepGoing = true;\n\n while (keepGoing) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process all complete messages separated by double newlines\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const lines = part\n .split('\\n')\n .filter(line => line.startsWith('data:'));\n\n const jsonString = lines\n .map(line => line.trim().slice(5).trim())\n .join('');\n try {\n const { event, data } = JSON.parse(jsonString);\n if (event === 'start') {\n if (currentConversation === TEMP_CONVERSATION_ID) {\n // If the conversation is temp, we need to set the new conversation id\n newConversationId = data?.conversation_id;\n }\n }\n\n if (event === 'token') {\n const content = data?.token || '';\n\n finalMessages.push(content);\n\n // Store streaming message\n const [humanMessage, aiMessage] =\n streamingConversations.current[currentConversation];\n streamingConversations.current[currentConversation] = [\n humanMessage,\n { ...aiMessage, content: aiMessage.content + content },\n ];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += content;\n lastMessage.name =\n data?.response_metadata?.model || selectedModel;\n lastMessage.timestamp = getTimestamp(\n data?.response_metadata?.created_at || Date.now(),\n );\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n\n if (event === 'end') {\n const documents = data?.referenced_documents || [];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n if (documents.length) {\n lastMessage.sources = {\n sources: documents.map((doc: ReferencedDocument) => ({\n title: doc.doc_title,\n link: doc.doc_url,\n body: doc.doc_description,\n })),\n };\n }\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn('Error parsing JSON:', error);\n if (typeof onComplete === 'function') {\n onComplete('Invalid JSON received');\n }\n }\n }\n }\n } catch (e) {\n setConversations(prevConversations => {\n const conversation = prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += e;\n lastMessage.error = {\n title: e.message,\n };\n lastMessage.timestamp = getTimestamp(Date.now());\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n finalMessages.push(`${e}`);\n\n return {\n ...prevConversations,\n [newConversationId.length > 0\n ? newConversationId\n : currentConversation]: updatedConversation,\n };\n });\n }\n // reset current streaming\n streamingConversations.current[currentConversation] = [];\n if (typeof onComplete === 'function') {\n onComplete(finalMessages.join(''));\n }\n // Swap temp conversation messages with new conversation\n\n if (currentConversation === TEMP_CONVERSATION_ID && newConversationId) {\n setConversations(prevConversations => {\n return {\n ...prevConversations,\n [newConversationId]: prevConversations[TEMP_CONVERSATION_ID],\n };\n });\n\n onStart?.(newConversationId);\n\n setConversations(prev => {\n const { temp, ...rest } = prev;\n return rest;\n });\n }\n },\n\n [\n avatar,\n userName,\n onComplete,\n onStart,\n selectedModel,\n createMessage,\n currentConversation,\n ],\n );\n\n return {\n conversationMessages: conversations[currentConversation] ?? [],\n handleInputPrompt,\n conversations,\n scrollToBottomRef,\n ...queryProps,\n };\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { useRef, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useCaptureFeedback } from './useCaptureFeedback.esm.js';
|
|
3
|
+
import { useFeedbackState } from './useFeedbackState.esm.js';
|
|
4
|
+
import { useFeedbackStatus } from './useFeedbackStatus.esm.js';
|
|
5
|
+
|
|
6
|
+
const quickResponses = {
|
|
7
|
+
positive: [
|
|
8
|
+
{ id: "1", content: "Helpful information" },
|
|
9
|
+
{ id: "2", content: "Easy to understand" },
|
|
10
|
+
{ id: "3", content: "Resolved my issue" }
|
|
11
|
+
],
|
|
12
|
+
negative: [
|
|
13
|
+
{ id: "1", content: `Didn\u2019t answer my question` },
|
|
14
|
+
{ id: "2", content: "Hard to understand" },
|
|
15
|
+
{ id: "3", content: "Not Helpful" }
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
const useFeedbackActions = (messages, conversationId, isStreaming) => {
|
|
19
|
+
const copyButtonRef = useRef(null);
|
|
20
|
+
const speakingButtonRef = useRef(null);
|
|
21
|
+
const currentSpeakingIdRef = useRef(null);
|
|
22
|
+
const { data: feedbackEnabled } = useFeedbackStatus();
|
|
23
|
+
const { state, ...dispatch } = useFeedbackState();
|
|
24
|
+
const { mutateAsync: captureFeedback } = useCaptureFeedback();
|
|
25
|
+
const getFeedbackForm = useCallback(
|
|
26
|
+
(messageId, sentiment) => ({
|
|
27
|
+
key: `feedback-${sentiment}`,
|
|
28
|
+
id: `feedback-${sentiment}-${messageId}`,
|
|
29
|
+
hasTextArea: true,
|
|
30
|
+
quickResponses: quickResponses[sentiment],
|
|
31
|
+
onSubmit: (quickResponse = "0", additionalFeedback) => {
|
|
32
|
+
const quickIndex = Number(quickResponse) - 1;
|
|
33
|
+
const quickText = quickResponse !== "0" ? quickResponses[sentiment]?.[quickIndex]?.content : undefined;
|
|
34
|
+
let user_feedback = "";
|
|
35
|
+
if (quickText && additionalFeedback) {
|
|
36
|
+
user_feedback = `${quickText}
|
|
37
|
+
Additional feedback: ${additionalFeedback}`;
|
|
38
|
+
} else if (quickText) {
|
|
39
|
+
user_feedback = quickText;
|
|
40
|
+
} else if (additionalFeedback) {
|
|
41
|
+
user_feedback = additionalFeedback;
|
|
42
|
+
}
|
|
43
|
+
const botAnswerId = Number(messageId.split("-").pop());
|
|
44
|
+
const userQuestionId = botAnswerId - 1;
|
|
45
|
+
const payload = {
|
|
46
|
+
conversation_id: conversationId,
|
|
47
|
+
user_feedback,
|
|
48
|
+
sentiment: sentiment === "positive" ? 1 : -1,
|
|
49
|
+
user_question: messages[userQuestionId].content || "",
|
|
50
|
+
llm_response: messages[botAnswerId].content || ""
|
|
51
|
+
};
|
|
52
|
+
captureFeedback(payload).then(() => {
|
|
53
|
+
dispatch.hideFeedbackForm(messageId, sentiment);
|
|
54
|
+
dispatch.showCompletionForm(messageId, sentiment);
|
|
55
|
+
dispatch.updateButtonState(messageId, {
|
|
56
|
+
sentiment,
|
|
57
|
+
feedbackSubmitted: true
|
|
58
|
+
});
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
dispatch.hideCompletionForm(messageId, sentiment);
|
|
61
|
+
}, 1e3);
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
onClose: () => {
|
|
66
|
+
dispatch.hideFeedbackForm(messageId, sentiment);
|
|
67
|
+
dispatch.resetButtonState(messageId);
|
|
68
|
+
dispatch.updateButtonState(messageId, { sentiment: undefined });
|
|
69
|
+
},
|
|
70
|
+
focusOnLoad: true
|
|
71
|
+
}),
|
|
72
|
+
[captureFeedback, conversationId, dispatch, messages]
|
|
73
|
+
);
|
|
74
|
+
const feedbackForms = useCallback(
|
|
75
|
+
(messageId) => ({
|
|
76
|
+
positive: getFeedbackForm(messageId, "positive"),
|
|
77
|
+
negative: getFeedbackForm(messageId, "negative")
|
|
78
|
+
}),
|
|
79
|
+
[getFeedbackForm]
|
|
80
|
+
);
|
|
81
|
+
const getUserFeedbackComplete = useCallback(
|
|
82
|
+
(messageId, sentiment) => {
|
|
83
|
+
const isVisible = state[messageId]?.completionForm?.[sentiment];
|
|
84
|
+
if (!isVisible) return undefined;
|
|
85
|
+
return {
|
|
86
|
+
id: `feebback-completion-${sentiment}-${messageId}`,
|
|
87
|
+
onClose: () => {
|
|
88
|
+
dispatch.hideCompletionForm(messageId, sentiment);
|
|
89
|
+
dispatch.resetButtonState(messageId);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
[state, dispatch]
|
|
94
|
+
);
|
|
95
|
+
const scrollToFeedbackForm = useCallback(
|
|
96
|
+
(messageId, sentiment) => {
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
const el = document.getElementById(
|
|
99
|
+
`feedback-${sentiment}-${messageId}`
|
|
100
|
+
);
|
|
101
|
+
if (el) {
|
|
102
|
+
el.scrollIntoView({ behavior: "auto", block: "start" });
|
|
103
|
+
el.focus();
|
|
104
|
+
}
|
|
105
|
+
}, 0);
|
|
106
|
+
},
|
|
107
|
+
[]
|
|
108
|
+
);
|
|
109
|
+
const speakMessage = useCallback(
|
|
110
|
+
(messageId, content) => {
|
|
111
|
+
if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {
|
|
112
|
+
window.speechSynthesis.cancel();
|
|
113
|
+
if (currentSpeakingIdRef.current && currentSpeakingIdRef.current !== messageId) {
|
|
114
|
+
dispatch.updateButtonState(currentSpeakingIdRef.current, {
|
|
115
|
+
isSpeaking: false
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
currentSpeakingIdRef.current = messageId;
|
|
120
|
+
const utterance = new SpeechSynthesisUtterance(content);
|
|
121
|
+
utterance.onstart = () => {
|
|
122
|
+
dispatch.updateButtonState(messageId, { isSpeaking: true });
|
|
123
|
+
};
|
|
124
|
+
utterance.onend = () => {
|
|
125
|
+
dispatch.updateButtonState(messageId, { isSpeaking: false });
|
|
126
|
+
currentSpeakingIdRef.current = null;
|
|
127
|
+
};
|
|
128
|
+
utterance.onerror = () => {
|
|
129
|
+
dispatch.updateButtonState(messageId, { isSpeaking: false });
|
|
130
|
+
currentSpeakingIdRef.current = null;
|
|
131
|
+
};
|
|
132
|
+
window.speechSynthesis.speak(utterance);
|
|
133
|
+
},
|
|
134
|
+
[dispatch]
|
|
135
|
+
);
|
|
136
|
+
const cancelSpeaking = useCallback(
|
|
137
|
+
(messageId) => {
|
|
138
|
+
window.speechSynthesis.cancel();
|
|
139
|
+
currentSpeakingIdRef.current = null;
|
|
140
|
+
if (messageId) {
|
|
141
|
+
dispatch.updateButtonState(messageId, { isSpeaking: false });
|
|
142
|
+
} else {
|
|
143
|
+
Object.keys(state).forEach((id) => {
|
|
144
|
+
if (state[id].buttonState?.isSpeaking) {
|
|
145
|
+
dispatch.updateButtonState(id, { isSpeaking: false });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
const el = document.getElementById(`listen-${messageId}`);
|
|
151
|
+
if (el) {
|
|
152
|
+
el.blur();
|
|
153
|
+
}
|
|
154
|
+
}, 10);
|
|
155
|
+
},
|
|
156
|
+
[state, dispatch]
|
|
157
|
+
);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
return () => {
|
|
160
|
+
cancelSpeaking();
|
|
161
|
+
};
|
|
162
|
+
}, [conversationId]);
|
|
163
|
+
return useMemo(() => {
|
|
164
|
+
return feedbackEnabled ? messages.map((message, index) => {
|
|
165
|
+
if (message.role === "user" || isStreaming && messages.length - 1 === index)
|
|
166
|
+
return message;
|
|
167
|
+
const messageId = `${conversationId}-${message.role}-${index}`;
|
|
168
|
+
const { sentiment, copied, isSpeaking } = state[messageId]?.buttonState || {};
|
|
169
|
+
const showFeedbackForm = state[messageId]?.feedbackForm?.[sentiment];
|
|
170
|
+
const userFeedbackForm = showFeedbackForm && feedbackForms(messageId)[sentiment];
|
|
171
|
+
const userFeedbackComplete = getUserFeedbackComplete(
|
|
172
|
+
messageId,
|
|
173
|
+
sentiment
|
|
174
|
+
);
|
|
175
|
+
const createSentimentAction = (selected) => ({
|
|
176
|
+
isClicked: sentiment === selected,
|
|
177
|
+
onClick: () => {
|
|
178
|
+
if (sentiment !== selected) {
|
|
179
|
+
dispatch.toggleFeedbackForm(messageId, selected);
|
|
180
|
+
dispatch.updateButtonState(messageId, { sentiment: selected });
|
|
181
|
+
scrollToFeedbackForm(messageId, sentiment);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"aria-expanded": sentiment === selected,
|
|
185
|
+
"aria-controls": showFeedbackForm ? `feedback-${sentiment}-${messageId}` : `feebback-completion-${sentiment}-${messageId}`
|
|
186
|
+
});
|
|
187
|
+
const copyAction = {
|
|
188
|
+
isClicked: copied,
|
|
189
|
+
ref: copyButtonRef,
|
|
190
|
+
tooltipContent: copied ? "Copied" : null,
|
|
191
|
+
onClick: async () => {
|
|
192
|
+
await window.navigator.clipboard.writeText(
|
|
193
|
+
message.content
|
|
194
|
+
);
|
|
195
|
+
dispatch.updateButtonState(messageId, {
|
|
196
|
+
copied: true
|
|
197
|
+
});
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
dispatch.updateButtonState(messageId, { copied: false });
|
|
200
|
+
copyButtonRef?.current?.blur();
|
|
201
|
+
}, 1e3);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const listenAction = {
|
|
205
|
+
isClicked: isSpeaking,
|
|
206
|
+
ref: speakingButtonRef,
|
|
207
|
+
id: `listen-${messageId}`,
|
|
208
|
+
onClick: () => {
|
|
209
|
+
const isCurrentlySpeaking = state[messageId]?.buttonState?.isSpeaking;
|
|
210
|
+
if (isCurrentlySpeaking) {
|
|
211
|
+
cancelSpeaking(messageId);
|
|
212
|
+
} else {
|
|
213
|
+
cancelSpeaking();
|
|
214
|
+
speakMessage(messageId, message.content);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
...message,
|
|
220
|
+
userFeedbackForm,
|
|
221
|
+
userFeedbackComplete,
|
|
222
|
+
actions: {
|
|
223
|
+
positive: createSentimentAction("positive"),
|
|
224
|
+
negative: createSentimentAction("negative"),
|
|
225
|
+
copy: copyAction,
|
|
226
|
+
listen: listenAction
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}) : messages;
|
|
230
|
+
}, [
|
|
231
|
+
conversationId,
|
|
232
|
+
messages,
|
|
233
|
+
state,
|
|
234
|
+
dispatch,
|
|
235
|
+
isStreaming,
|
|
236
|
+
speakMessage,
|
|
237
|
+
feedbackForms,
|
|
238
|
+
cancelSpeaking,
|
|
239
|
+
feedbackEnabled,
|
|
240
|
+
getUserFeedbackComplete,
|
|
241
|
+
scrollToFeedbackForm
|
|
242
|
+
]);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export { useFeedbackActions };
|
|
246
|
+
//# sourceMappingURL=useFeedbackActions.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFeedbackActions.esm.js","sources":["../../src/hooks/useFeedbackActions.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 */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport QuickResponse from '@patternfly/chatbot/dist/cjs/Message/QuickResponse/QuickResponse';\nimport { UserFeedbackProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedback';\nimport { UserFeedbackCompleteProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedbackComplete';\n\nimport { useCaptureFeedback } from '../hooks/useCaptureFeedback';\nimport { CaptureFeedback } from '../types';\nimport { Sentiment, useFeedbackState } from './useFeedbackState';\nimport { useFeedbackStatus } from './useFeedbackStatus';\n\nconst quickResponses: { [key in Sentiment]: QuickResponse[] } = {\n positive: [\n { id: '1', content: 'Helpful information' },\n { id: '2', content: 'Easy to understand' },\n { id: '3', content: 'Resolved my issue' },\n ],\n negative: [\n { id: '1', content: `Didn’t answer my question` },\n { id: '2', content: 'Hard to understand' },\n { id: '3', content: 'Not Helpful' },\n ],\n};\n\nexport const useFeedbackActions = <T extends MessageProps>(\n messages: T[],\n conversationId: string,\n isStreaming: boolean,\n): T[] => {\n const copyButtonRef = useRef<HTMLButtonElement>(null);\n const speakingButtonRef = useRef<HTMLButtonElement>(null);\n const currentSpeakingIdRef = useRef<string | null>(null);\n\n const { data: feedbackEnabled } = useFeedbackStatus();\n const { state, ...dispatch } = useFeedbackState();\n const { mutateAsync: captureFeedback } = useCaptureFeedback();\n\n const getFeedbackForm = useCallback(\n (messageId: string, sentiment: 'positive' | 'negative') => ({\n key: `feedback-${sentiment}`,\n id: `feedback-${sentiment}-${messageId}`,\n hasTextArea: true,\n quickResponses: quickResponses[sentiment],\n onSubmit: (\n quickResponse: string | undefined = '0',\n additionalFeedback: string | undefined,\n ) => {\n const quickIndex = Number(quickResponse) - 1;\n const quickText =\n quickResponse !== '0'\n ? quickResponses[sentiment]?.[quickIndex]?.content\n : undefined;\n\n let user_feedback = '';\n\n if (quickText && additionalFeedback) {\n user_feedback = `${quickText}\\nAdditional feedback: ${additionalFeedback}`;\n } else if (quickText) {\n user_feedback = quickText;\n } else if (additionalFeedback) {\n user_feedback = additionalFeedback;\n }\n\n const botAnswerId = Number(messageId.split('-').pop());\n const userQuestionId = botAnswerId - 1;\n\n const payload: CaptureFeedback = {\n conversation_id: conversationId,\n user_feedback,\n sentiment: sentiment === 'positive' ? 1 : -1,\n user_question: messages[userQuestionId].content || '',\n llm_response: messages[botAnswerId].content || '',\n };\n\n captureFeedback(payload).then(() => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.showCompletionForm(messageId, sentiment);\n dispatch.updateButtonState(messageId, {\n sentiment: sentiment,\n feedbackSubmitted: true,\n });\n\n setTimeout(() => {\n dispatch.hideCompletionForm(messageId, sentiment);\n }, 1000);\n });\n },\n\n // eslint-disable-next-line no-console\n onClose: () => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n dispatch.updateButtonState(messageId, { sentiment: undefined });\n },\n focusOnLoad: true,\n }),\n [captureFeedback, conversationId, dispatch, messages],\n );\n\n const feedbackForms = useCallback(\n (messageId: string): { [key in Sentiment]: UserFeedbackProps } => ({\n positive: getFeedbackForm(messageId, 'positive'),\n negative: getFeedbackForm(messageId, 'negative'),\n }),\n [getFeedbackForm],\n );\n\n const getUserFeedbackComplete = useCallback(\n (\n messageId: string,\n sentiment: Sentiment,\n ): UserFeedbackCompleteProps | undefined => {\n const isVisible = state[messageId]?.completionForm?.[sentiment];\n if (!isVisible) return undefined;\n\n return {\n id: `feebback-completion-${sentiment}-${messageId}`,\n onClose: () => {\n dispatch.hideCompletionForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n },\n };\n },\n [state, dispatch],\n );\n\n const scrollToFeedbackForm = useCallback(\n (messageId: string, sentiment: Sentiment) => {\n setTimeout(() => {\n const el = document.getElementById(\n `feedback-${sentiment}-${messageId}`,\n );\n if (el) {\n el.scrollIntoView({ behavior: 'auto', block: 'start' });\n el.focus();\n }\n }, 0);\n },\n [],\n );\n\n const speakMessage = useCallback(\n (messageId: string, content: string) => {\n if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {\n window.speechSynthesis.cancel();\n\n if (\n currentSpeakingIdRef.current &&\n currentSpeakingIdRef.current !== messageId\n ) {\n dispatch.updateButtonState(currentSpeakingIdRef.current, {\n isSpeaking: false,\n });\n }\n }\n\n // Track the current SpeakingId\n currentSpeakingIdRef.current = messageId;\n\n const utterance = new SpeechSynthesisUtterance(content);\n\n utterance.onstart = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: true });\n };\n\n utterance.onend = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n utterance.onerror = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n window.speechSynthesis.speak(utterance);\n },\n [dispatch],\n );\n\n const cancelSpeaking = useCallback(\n (messageId?: string) => {\n window.speechSynthesis.cancel();\n\n currentSpeakingIdRef.current = null;\n\n if (messageId) {\n // Cancel just this one message\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n } else {\n // Cancel all messages\n Object.keys(state).forEach(id => {\n if (state[id].buttonState?.isSpeaking) {\n dispatch.updateButtonState(id, { isSpeaking: false });\n }\n });\n }\n setTimeout(() => {\n const el = document.getElementById(`listen-${messageId}`);\n if (el) {\n el.blur();\n }\n }, 10);\n },\n [state, dispatch],\n );\n\n useEffect(() => {\n return () => {\n // Stop any ongoing speech when component unmounts\n cancelSpeaking();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [conversationId]);\n\n return useMemo(() => {\n return feedbackEnabled\n ? messages.map((message, index) => {\n if (\n message.role === 'user' ||\n (isStreaming && messages.length - 1 === index)\n )\n return message;\n\n const messageId = `${conversationId}-${message.role}-${index}`;\n const { sentiment, copied, isSpeaking } =\n state[messageId]?.buttonState || {};\n const showFeedbackForm = state[messageId]?.feedbackForm?.[sentiment];\n const userFeedbackForm =\n showFeedbackForm && feedbackForms(messageId)[sentiment];\n\n const userFeedbackComplete = getUserFeedbackComplete(\n messageId,\n sentiment,\n );\n\n const createSentimentAction = (selected: Sentiment) => ({\n isClicked: sentiment === selected,\n onClick: () => {\n if (sentiment !== selected) {\n dispatch.toggleFeedbackForm(messageId, selected);\n dispatch.updateButtonState(messageId, { sentiment: selected });\n scrollToFeedbackForm(messageId, sentiment);\n }\n },\n 'aria-expanded': sentiment === selected,\n 'aria-controls': showFeedbackForm\n ? `feedback-${sentiment}-${messageId}`\n : `feebback-completion-${sentiment}-${messageId}`,\n });\n\n const copyAction = {\n isClicked: copied,\n ref: copyButtonRef,\n tooltipContent: copied ? 'Copied' : null,\n onClick: async () => {\n await window.navigator.clipboard.writeText(\n message.content as string,\n );\n dispatch.updateButtonState(messageId, {\n copied: true,\n });\n\n setTimeout(() => {\n dispatch.updateButtonState(messageId, { copied: false });\n copyButtonRef?.current?.blur();\n }, 1000);\n },\n };\n\n const listenAction = {\n isClicked: isSpeaking,\n ref: speakingButtonRef,\n id: `listen-${messageId}`,\n onClick: () => {\n const isCurrentlySpeaking =\n state[messageId]?.buttonState?.isSpeaking;\n\n if (isCurrentlySpeaking) {\n // Stop if it's already speaking this message\n cancelSpeaking(messageId);\n } else {\n cancelSpeaking();\n speakMessage(messageId, message.content as string);\n }\n },\n };\n\n return {\n ...message,\n userFeedbackForm,\n userFeedbackComplete,\n actions: {\n positive: createSentimentAction('positive'),\n negative: createSentimentAction('negative'),\n copy: copyAction,\n listen: listenAction,\n },\n };\n })\n : messages;\n }, [\n conversationId,\n messages,\n state,\n dispatch,\n isStreaming,\n speakMessage,\n feedbackForms,\n cancelSpeaking,\n feedbackEnabled,\n getUserFeedbackComplete,\n scrollToFeedbackForm,\n ]);\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useReducer } from 'react';
|
|
2
|
+
|
|
3
|
+
function reducer(state, action) {
|
|
4
|
+
const entry = state[action.messageId] ?? {
|
|
5
|
+
feedbackForm: { positive: false, negative: false },
|
|
6
|
+
completionForm: { positive: false, negative: false },
|
|
7
|
+
buttonState: {}
|
|
8
|
+
};
|
|
9
|
+
switch (action.type) {
|
|
10
|
+
case "SHOW_FEEDBACK_FORM":
|
|
11
|
+
return {
|
|
12
|
+
...state,
|
|
13
|
+
[action.messageId]: {
|
|
14
|
+
...entry,
|
|
15
|
+
feedbackForm: { ...entry.feedbackForm, [action.sentiment]: true }
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
case "HIDE_FEEDBACK_FORM":
|
|
19
|
+
return {
|
|
20
|
+
...state,
|
|
21
|
+
[action.messageId]: {
|
|
22
|
+
...entry,
|
|
23
|
+
feedbackForm: { ...entry.feedbackForm, [action.sentiment]: false }
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
case "SHOW_COMPLETION_FORM":
|
|
27
|
+
return {
|
|
28
|
+
...state,
|
|
29
|
+
[action.messageId]: {
|
|
30
|
+
...entry,
|
|
31
|
+
completionForm: { ...entry.completionForm, [action.sentiment]: true }
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
case "HIDE_COMPLETION_FORM":
|
|
35
|
+
return {
|
|
36
|
+
...state,
|
|
37
|
+
[action.messageId]: {
|
|
38
|
+
...entry,
|
|
39
|
+
completionForm: {
|
|
40
|
+
...entry.completionForm,
|
|
41
|
+
[action.sentiment]: false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
case "UPDATE_BUTTON_STATE":
|
|
46
|
+
return {
|
|
47
|
+
...state,
|
|
48
|
+
[action.messageId]: {
|
|
49
|
+
...entry,
|
|
50
|
+
buttonState: { ...entry.buttonState, ...action.payload }
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
case "RESET_BUTTON_STATE": {
|
|
54
|
+
const { [action.messageId]: _, ...rest } = state;
|
|
55
|
+
return rest;
|
|
56
|
+
}
|
|
57
|
+
default:
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function useFeedbackState() {
|
|
62
|
+
const [state, dispatch] = useReducer(reducer, {});
|
|
63
|
+
const showFeedbackForm = (messageId, sentiment) => dispatch({ type: "SHOW_FEEDBACK_FORM", messageId, sentiment });
|
|
64
|
+
const hideFeedbackForm = (messageId, sentiment) => dispatch({ type: "HIDE_FEEDBACK_FORM", messageId, sentiment });
|
|
65
|
+
const showCompletionForm = (messageId, sentiment) => dispatch({ type: "SHOW_COMPLETION_FORM", messageId, sentiment });
|
|
66
|
+
const hideCompletionForm = (messageId, sentiment) => dispatch({ type: "HIDE_COMPLETION_FORM", messageId, sentiment });
|
|
67
|
+
const updateButtonState = (messageId, payload) => dispatch({ type: "UPDATE_BUTTON_STATE", messageId, payload });
|
|
68
|
+
const resetButtonState = (messageId) => dispatch({ type: "RESET_BUTTON_STATE", messageId });
|
|
69
|
+
const getFeedbackState = (messageId) => state[messageId] ?? {
|
|
70
|
+
feedbackForm: { positive: false, negative: false },
|
|
71
|
+
completionForm: { positive: false, negative: false },
|
|
72
|
+
buttonState: { sentiment: "positive", copied: false }
|
|
73
|
+
};
|
|
74
|
+
const toggleFeedbackForm = (messageId, sentimentToShow) => {
|
|
75
|
+
const opposite = sentimentToShow === "positive" ? "negative" : "positive";
|
|
76
|
+
hideFeedbackForm(messageId, opposite);
|
|
77
|
+
showFeedbackForm(messageId, sentimentToShow);
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
state,
|
|
81
|
+
getFeedbackState,
|
|
82
|
+
showFeedbackForm,
|
|
83
|
+
hideFeedbackForm,
|
|
84
|
+
toggleFeedbackForm,
|
|
85
|
+
showCompletionForm,
|
|
86
|
+
hideCompletionForm,
|
|
87
|
+
updateButtonState,
|
|
88
|
+
resetButtonState
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { useFeedbackState };
|
|
93
|
+
//# sourceMappingURL=useFeedbackState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFeedbackState.esm.js","sources":["../../src/hooks/useFeedbackState.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useReducer } from 'react';\n\nexport type Sentiment = 'positive' | 'negative';\n\ntype FeedbackButtonState = {\n sentiment: Sentiment;\n copied: boolean;\n isSpeaking: boolean;\n feedbackSubmitted?: boolean;\n};\n\ntype MessageState = {\n feedbackForm: Record<Sentiment, boolean>;\n completionForm: Record<Sentiment, boolean>;\n buttonState: FeedbackButtonState;\n};\n\ntype State = Record<string, MessageState>;\n\ntype Action =\n | { type: 'SHOW_FEEDBACK_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'HIDE_FEEDBACK_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'SHOW_COMPLETION_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'HIDE_COMPLETION_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'RESET_BUTTON_STATE'; messageId: string }\n | {\n type: 'UPDATE_BUTTON_STATE';\n messageId: string;\n payload: Partial<FeedbackButtonState>;\n };\n\nfunction reducer(state: State, action: Action): State {\n const entry = state[action.messageId] ?? {\n feedbackForm: { positive: false, negative: false },\n completionForm: { positive: false, negative: false },\n buttonState: {},\n };\n\n switch (action.type) {\n case 'SHOW_FEEDBACK_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n feedbackForm: { ...entry.feedbackForm, [action.sentiment]: true },\n },\n };\n case 'HIDE_FEEDBACK_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n feedbackForm: { ...entry.feedbackForm, [action.sentiment]: false },\n },\n };\n case 'SHOW_COMPLETION_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n completionForm: { ...entry.completionForm, [action.sentiment]: true },\n },\n };\n case 'HIDE_COMPLETION_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n completionForm: {\n ...entry.completionForm,\n [action.sentiment]: false,\n },\n },\n };\n case 'UPDATE_BUTTON_STATE':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n buttonState: { ...entry.buttonState, ...action.payload },\n },\n };\n case 'RESET_BUTTON_STATE': {\n const { [action.messageId]: _, ...rest } = state;\n return rest;\n }\n default:\n return state;\n }\n}\n\nexport function useFeedbackState() {\n const [state, dispatch] = useReducer(reducer, {});\n\n const showFeedbackForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'SHOW_FEEDBACK_FORM', messageId, sentiment });\n\n const hideFeedbackForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'HIDE_FEEDBACK_FORM', messageId, sentiment });\n\n const showCompletionForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'SHOW_COMPLETION_FORM', messageId, sentiment });\n\n const hideCompletionForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'HIDE_COMPLETION_FORM', messageId, sentiment });\n\n const updateButtonState = (\n messageId: string,\n payload: Partial<FeedbackButtonState>,\n ) => dispatch({ type: 'UPDATE_BUTTON_STATE', messageId, payload });\n\n const resetButtonState = (messageId: string) =>\n dispatch({ type: 'RESET_BUTTON_STATE', messageId });\n\n const getFeedbackState = (messageId: string) =>\n state[messageId] ?? {\n feedbackForm: { positive: false, negative: false },\n completionForm: { positive: false, negative: false },\n buttonState: { sentiment: 'positive', copied: false },\n };\n\n const toggleFeedbackForm = (\n messageId: string,\n sentimentToShow: Sentiment,\n ) => {\n const opposite: Sentiment =\n sentimentToShow === 'positive' ? 'negative' : 'positive';\n\n hideFeedbackForm(messageId, opposite);\n showFeedbackForm(messageId, sentimentToShow);\n };\n\n return {\n state,\n getFeedbackState,\n showFeedbackForm,\n hideFeedbackForm,\n toggleFeedbackForm,\n showCompletionForm,\n hideCompletionForm,\n updateButtonState,\n resetButtonState,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
2
|
+
import { useQuery } from '@tanstack/react-query';
|
|
3
|
+
import { lightspeedApiRef } from '../api/api.esm.js';
|
|
4
|
+
|
|
5
|
+
const useFeedbackStatus = () => {
|
|
6
|
+
const lightspeedApi = useApi(lightspeedApiRef);
|
|
7
|
+
return useQuery({
|
|
8
|
+
queryKey: ["feedbackStatus"],
|
|
9
|
+
queryFn: async () => await lightspeedApi.getFeedbackStatus()
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { useFeedbackStatus };
|
|
14
|
+
//# sourceMappingURL=useFeedbackStatus.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFeedbackStatus.esm.js","sources":["../../src/hooks/useFeedbackStatus.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { useQuery } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\n\n// Fetch feedback status\nexport const useFeedbackStatus = () => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['feedbackStatus'],\n queryFn: async () => await lightspeedApi.getFeedbackStatus(),\n });\n};\n"],"names":[],"mappings":";;;;AAsBO,MAAM,oBAAoB,MAAM;AACrC,EAAM,MAAA,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAC7C,EAAA,OAAO,QAAS,CAAA;AAAA,IACd,QAAA,EAAU,CAAC,gBAAgB,CAAA;AAAA,IAC3B,OAAS,EAAA,YAAY,MAAM,aAAA,CAAc,iBAAkB;AAAA,GAC5D,CAAA;AACH;;;;"}
|
package/dist/types.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.esm.js","sources":["../src/types.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 { SourcesCardProps } from '@patternfly/chatbot';\nimport { AlertProps } from '@patternfly/react-core';\n\nexport type Conversations = {\n [key: string]: {\n user: string;\n bot: string;\n model: string;\n loading: boolean;\n timestamp: string;\n botTimestamp: string;\n error?: AlertProps;\n };\n};\n\nexport type
|
|
1
|
+
{"version":3,"file":"types.esm.js","sources":["../src/types.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 { SourcesCardProps } from '@patternfly/chatbot';\nimport { AlertProps } from '@patternfly/react-core';\n\nexport type Conversations = {\n [key: string]: {\n user: string;\n bot: string;\n model: string;\n loading: boolean;\n timestamp: string;\n botTimestamp: string;\n error?: AlertProps;\n };\n};\n\nexport type ReferencedDocument = {\n doc_title: string;\n doc_url: string;\n doc_description?: string;\n};\n\nexport type ReferencedDocuments = ReferencedDocument[];\nexport interface BaseMessage {\n name: string;\n type: string;\n id: number;\n content: string;\n response_metadata: {\n model?: string;\n created_at: number;\n role?: string;\n };\n sources?: SourcesCardProps;\n additional_kwargs: {\n referenced_documents?: ReferencedDocuments;\n [_key: string]: any;\n };\n error?: AlertProps;\n}\nexport type ConversationSummary = {\n conversation_id: string;\n last_message_timestamp: number;\n topic_summary: string;\n};\n\nexport enum SupportedFileType {\n JSON = 'application/json',\n YAML = 'application/x-yaml',\n TEXT = 'text/plain',\n XML = 'text/xml',\n}\nexport interface FileContent {\n content: string;\n type: string;\n name: string;\n}\n\nexport type Attachment = {\n attachment_type: string;\n content_type: string;\n content: string;\n};\n\nexport type ConversationList = ConversationSummary[];\n\nexport type SamplePrompts = {\n title: string;\n message: string;\n}[];\n\nexport type CaptureFeedback = {\n conversation_id: string;\n user_question: string;\n llm_response: string;\n user_feedback: string;\n sentiment: number;\n};\n"],"names":["SupportedFileType"],"mappings":"AA6DY,IAAA,iBAAA,qBAAAA,kBAAL,KAAA;AACL,EAAAA,mBAAA,MAAO,CAAA,GAAA,kBAAA;AACP,EAAAA,mBAAA,MAAO,CAAA,GAAA,oBAAA;AACP,EAAAA,mBAAA,MAAO,CAAA,GAAA,YAAA;AACP,EAAAA,mBAAA,KAAM,CAAA,GAAA,UAAA;AAJI,EAAAA,OAAAA,kBAAAA;AAAA,CAAA,EAAA,iBAAA,IAAA,EAAA;;;;"}
|
|
@@ -83,13 +83,12 @@ const transformDocumentsToSources = (referenced_documents) => {
|
|
|
83
83
|
return undefined;
|
|
84
84
|
}
|
|
85
85
|
return {
|
|
86
|
-
sources: referenced_documents.map(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
86
|
+
sources: referenced_documents.map((doc) => ({
|
|
87
|
+
body: doc.doc_description,
|
|
88
|
+
title: doc.doc_title,
|
|
89
|
+
link: doc?.doc_url,
|
|
90
|
+
isExternal: true
|
|
91
|
+
}))
|
|
93
92
|
};
|
|
94
93
|
};
|
|
95
94
|
const getDayDifference = (sourceTime, targetTime) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lightspeed-chatbox-utils.esm.js","sources":["../../src/utils/lightspeed-chatbox-utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Conversation, SourcesCardProps } from '@patternfly/chatbot';\nimport { PopoverProps } from '@patternfly/react-core';\n\nimport {\n BaseMessage,\n ConversationList,\n ConversationSummary,\n ReferencedDocuments,\n} from '../types';\n\nexport const getFootnoteProps = (additionalClassName: string) => ({\n label: 'Always check AI/LLM generated responses for accuracy prior to use.',\n popover: {\n popoverProps: {\n className: additionalClassName ?? '',\n } as PopoverProps,\n title: 'Verify accuracy',\n description: `While Developer Lightspeed strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,\n bannerImage: {\n src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',\n alt: 'Example image for footnote popover',\n },\n cta: {\n label: 'Got it',\n onClick: () => {},\n },\n link: {\n label: 'Learn more',\n url: 'https://www.redhat.com/',\n },\n },\n});\n\nexport const getTimestampVariablesString = (v: number) => {\n if (v < 10) {\n return `0${v}`;\n }\n return `${v}`;\n};\n\nexport const getTimestamp = (unix_timestamp: number) => {\n if (typeof unix_timestamp !== 'number' || isNaN(unix_timestamp)) {\n // eslint-disable-next-line no-console\n console.error('Invalid Unix timestamp provided');\n return '';\n }\n\n const a = new Date(unix_timestamp);\n const month = getTimestampVariablesString(a.getMonth() + 1);\n const year = a.getFullYear();\n const date = getTimestampVariablesString(a.getDate());\n const hour = getTimestampVariablesString(a.getHours());\n const min = getTimestampVariablesString(a.getMinutes());\n const sec = getTimestampVariablesString(a.getSeconds());\n const time = `${date}/${month}/${year}, ${hour}:${min}:${sec}`;\n return time;\n};\n\nexport const splitJsonStrings = (jsonString: string): string[] => {\n const chunks = jsonString.split('}{');\n\n if (chunks.length <= 1) {\n return [jsonString];\n }\n\n return chunks.map((chunk, index, arr) => {\n if (index === 0) {\n return `${chunk}}`;\n } else if (index === arr.length - 1) {\n return `{${chunk}`;\n }\n return `{${chunk}}`;\n });\n};\n\ntype MessageProps = {\n content: string;\n timestamp: string;\n name?: string;\n avatar?: string | any;\n isLoading?: boolean;\n error?: {\n title: string;\n };\n sources?: SourcesCardProps;\n};\n\nexport const createMessage = ({\n role,\n name = 'Guest',\n avatar,\n isLoading = false,\n content,\n timestamp,\n error,\n sources,\n}: MessageProps & { role: 'user' | 'bot' }) => ({\n role,\n name,\n avatar,\n isLoading,\n content,\n timestamp,\n error,\n sources,\n});\n\nexport const createUserMessage = (props: MessageProps) =>\n createMessage({\n ...props,\n role: 'user',\n name: props.name ?? 'Guest',\n });\n\nexport const createBotMessage = (props: MessageProps) =>\n createMessage({\n ...props,\n role: 'bot',\n });\n\nexport const getMessageData = (message: BaseMessage) => {\n return {\n model: message?.response_metadata?.model,\n content: message?.content || '',\n timestamp: getTimestamp(message?.response_metadata?.created_at * 1000),\n referencedDocuments: message?.additional_kwargs?.referenced_documents ?? [],\n };\n};\n\nexport const transformDocumentsToSources = (\n referenced_documents: ReferencedDocuments,\n): SourcesCardProps | undefined => {\n if (!referenced_documents || referenced_documents?.length === 0) {\n return undefined;\n }\n return {\n sources: referenced_documents.map(\n (doc: { doc_title: string; doc_url: string }) => ({\n title: doc.doc_title,\n link: doc?.doc_url,\n isExternal: true,\n }),\n ),\n };\n};\n\nexport const getDayDifference = (sourceTime: number, targetTime: number) => {\n const sourceDate = new Date(sourceTime);\n const targetDate = new Date(targetTime);\n\n sourceDate.setHours(0, 0, 0, 0);\n targetDate.setHours(0, 0, 0, 0);\n\n const timeDifference = sourceDate.getTime() - targetDate.getTime();\n\n return Math.floor(timeDifference / (1000 * 60 * 60 * 24));\n};\n\nexport const getCategorizeMessages = (\n messages: ConversationList,\n addProps: (c: ConversationSummary) => { [k: string]: any },\n): { [k: string]: Conversation[] } => {\n const now: any = new Date();\n const today = now.toDateString();\n\n const categorizedMessages: { [k: string]: Conversation[] } = {\n Today: [],\n Yesterday: [],\n 'Previous 7 Days': [],\n 'Previous 30 Days': [],\n };\n messages\n .sort((a, b) => b.last_message_timestamp - a.last_message_timestamp)\n .forEach(c => {\n const messageDate = new Date(c.last_message_timestamp * 1000);\n const messageDayString = messageDate.toDateString();\n const dayDifference = getDayDifference(\n now,\n c.last_message_timestamp * 1000,\n );\n const message: Conversation = {\n id: c.conversation_id,\n text: c.topic_summary,\n label: 'Options',\n ...addProps(c),\n };\n\n if (messageDayString === today) {\n categorizedMessages.Today.push(message);\n } else if (dayDifference === 1) {\n categorizedMessages.Yesterday.push(message);\n } else if (dayDifference <= 7) {\n categorizedMessages['Previous 7 Days'].push(message);\n } else if (dayDifference <= 30) {\n categorizedMessages['Previous 30 Days'].push(message);\n } else {\n // handle month-wise grouping\n const monthYear = messageDate.toLocaleString('default', {\n month: 'long',\n year: 'numeric',\n });\n if (!categorizedMessages[monthYear]) {\n categorizedMessages[monthYear] = [];\n }\n categorizedMessages[monthYear].push(message);\n }\n });\n\n const filteredCategories = Object.keys(categorizedMessages).reduce(\n (result, category) => {\n if (categorizedMessages[category].length > 0) {\n result[category] = categorizedMessages[category];\n }\n return result;\n },\n {} as any,\n );\n\n return filteredCategories;\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lightspeed-chatbox-utils.esm.js","sources":["../../src/utils/lightspeed-chatbox-utils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Conversation, SourcesCardProps } from '@patternfly/chatbot';\nimport { PopoverProps } from '@patternfly/react-core';\n\nimport {\n BaseMessage,\n ConversationList,\n ConversationSummary,\n ReferencedDocument,\n ReferencedDocuments,\n} from '../types';\n\nexport const getFootnoteProps = (additionalClassName: string) => ({\n label: 'Always check AI/LLM generated responses for accuracy prior to use.',\n popover: {\n popoverProps: {\n className: additionalClassName ?? '',\n } as PopoverProps,\n title: 'Verify accuracy',\n description: `While Developer Lightspeed strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,\n bannerImage: {\n src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',\n alt: 'Example image for footnote popover',\n },\n cta: {\n label: 'Got it',\n onClick: () => {},\n },\n link: {\n label: 'Learn more',\n url: 'https://www.redhat.com/',\n },\n },\n});\n\nexport const getTimestampVariablesString = (v: number) => {\n if (v < 10) {\n return `0${v}`;\n }\n return `${v}`;\n};\n\nexport const getTimestamp = (unix_timestamp: number) => {\n if (typeof unix_timestamp !== 'number' || isNaN(unix_timestamp)) {\n // eslint-disable-next-line no-console\n console.error('Invalid Unix timestamp provided');\n return '';\n }\n\n const a = new Date(unix_timestamp);\n const month = getTimestampVariablesString(a.getMonth() + 1);\n const year = a.getFullYear();\n const date = getTimestampVariablesString(a.getDate());\n const hour = getTimestampVariablesString(a.getHours());\n const min = getTimestampVariablesString(a.getMinutes());\n const sec = getTimestampVariablesString(a.getSeconds());\n const time = `${date}/${month}/${year}, ${hour}:${min}:${sec}`;\n return time;\n};\n\nexport const splitJsonStrings = (jsonString: string): string[] => {\n const chunks = jsonString.split('}{');\n\n if (chunks.length <= 1) {\n return [jsonString];\n }\n\n return chunks.map((chunk, index, arr) => {\n if (index === 0) {\n return `${chunk}}`;\n } else if (index === arr.length - 1) {\n return `{${chunk}`;\n }\n return `{${chunk}}`;\n });\n};\n\ntype MessageProps = {\n content: string;\n timestamp: string;\n name?: string;\n avatar?: string | any;\n isLoading?: boolean;\n error?: {\n title: string;\n };\n sources?: SourcesCardProps;\n};\n\nexport const createMessage = ({\n role,\n name = 'Guest',\n avatar,\n isLoading = false,\n content,\n timestamp,\n error,\n sources,\n}: MessageProps & { role: 'user' | 'bot' }) => ({\n role,\n name,\n avatar,\n isLoading,\n content,\n timestamp,\n error,\n sources,\n});\n\nexport const createUserMessage = (props: MessageProps) =>\n createMessage({\n ...props,\n role: 'user',\n name: props.name ?? 'Guest',\n });\n\nexport const createBotMessage = (props: MessageProps) =>\n createMessage({\n ...props,\n role: 'bot',\n });\n\nexport const getMessageData = (message: BaseMessage) => {\n return {\n model: message?.response_metadata?.model,\n content: message?.content || '',\n timestamp: getTimestamp(message?.response_metadata?.created_at * 1000),\n referencedDocuments: message?.additional_kwargs?.referenced_documents ?? [],\n };\n};\n\nexport const transformDocumentsToSources = (\n referenced_documents: ReferencedDocuments,\n): SourcesCardProps | undefined => {\n if (!referenced_documents || referenced_documents?.length === 0) {\n return undefined;\n }\n return {\n sources: referenced_documents.map((doc: ReferencedDocument) => ({\n body: doc.doc_description,\n title: doc.doc_title,\n link: doc?.doc_url,\n isExternal: true,\n })),\n };\n};\n\nexport const getDayDifference = (sourceTime: number, targetTime: number) => {\n const sourceDate = new Date(sourceTime);\n const targetDate = new Date(targetTime);\n\n sourceDate.setHours(0, 0, 0, 0);\n targetDate.setHours(0, 0, 0, 0);\n\n const timeDifference = sourceDate.getTime() - targetDate.getTime();\n\n return Math.floor(timeDifference / (1000 * 60 * 60 * 24));\n};\n\nexport const getCategorizeMessages = (\n messages: ConversationList,\n addProps: (c: ConversationSummary) => { [k: string]: any },\n): { [k: string]: Conversation[] } => {\n const now: any = new Date();\n const today = now.toDateString();\n\n const categorizedMessages: { [k: string]: Conversation[] } = {\n Today: [],\n Yesterday: [],\n 'Previous 7 Days': [],\n 'Previous 30 Days': [],\n };\n messages\n .sort((a, b) => b.last_message_timestamp - a.last_message_timestamp)\n .forEach(c => {\n const messageDate = new Date(c.last_message_timestamp * 1000);\n const messageDayString = messageDate.toDateString();\n const dayDifference = getDayDifference(\n now,\n c.last_message_timestamp * 1000,\n );\n const message: Conversation = {\n id: c.conversation_id,\n text: c.topic_summary,\n label: 'Options',\n ...addProps(c),\n };\n\n if (messageDayString === today) {\n categorizedMessages.Today.push(message);\n } else if (dayDifference === 1) {\n categorizedMessages.Yesterday.push(message);\n } else if (dayDifference <= 7) {\n categorizedMessages['Previous 7 Days'].push(message);\n } else if (dayDifference <= 30) {\n categorizedMessages['Previous 30 Days'].push(message);\n } else {\n // handle month-wise grouping\n const monthYear = messageDate.toLocaleString('default', {\n month: 'long',\n year: 'numeric',\n });\n if (!categorizedMessages[monthYear]) {\n categorizedMessages[monthYear] = [];\n }\n categorizedMessages[monthYear].push(message);\n }\n });\n\n const filteredCategories = Object.keys(categorizedMessages).reduce(\n (result, category) => {\n if (categorizedMessages[category].length > 0) {\n result[category] = categorizedMessages[category];\n }\n return result;\n },\n {} as any,\n );\n\n return filteredCategories;\n};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@red-hat-developer-hub/backstage-plugin-lightspeed",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"main": "dist/index.esm.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"@material-ui/core": "^4.9.13",
|
|
45
45
|
"@material-ui/lab": "^4.0.0-alpha.61",
|
|
46
46
|
"@mui/icons-material": "^6.1.8",
|
|
47
|
-
"@patternfly/chatbot": "6.3.0-prerelease.
|
|
47
|
+
"@patternfly/chatbot": "6.3.0-prerelease.25",
|
|
48
48
|
"@patternfly/react-core": "6.3.0-prerelease.17",
|
|
49
|
-
"@red-hat-developer-hub/backstage-plugin-lightspeed-common": "^0.3.
|
|
49
|
+
"@red-hat-developer-hub/backstage-plugin-lightspeed-common": "^0.3.3",
|
|
50
50
|
"@tanstack/react-query": "^5.59.15",
|
|
51
51
|
"openai": "^4.52.6",
|
|
52
52
|
"react-markdown": "^9.0.1",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@testing-library/react": "^15.0.0",
|
|
70
70
|
"@testing-library/user-event": "14.6.1",
|
|
71
71
|
"msw": "1.3.5",
|
|
72
|
-
"prettier": "3.
|
|
72
|
+
"prettier": "3.6.2",
|
|
73
73
|
"react": "16.13.1 || ^17.0.0 || ^18.0.0",
|
|
74
74
|
"react-dom": "16.13.1 || ^17.0.0 || ^18.0.0",
|
|
75
75
|
"react-router-dom": "^6.0.0"
|