diff --git a/packages/chat/__stories__/feedback-container.stories.js b/packages/chat/__stories__/feedback-container.stories.js
new file mode 100644
index 00000000..87c6046f
--- /dev/null
+++ b/packages/chat/__stories__/feedback-container.stories.js
@@ -0,0 +1,57 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import '../components/feedback/feedback-container';
+
+import { html } from 'lit';
+/**
+ * More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
+ */
+
+export default {
+ title: 'Components/Chat/Feedback',
+ tags: ['autodocs'],
+};
+
+/**
+ * More on writing stories with args: https://storybook.js.org/docs/web-components/writing-stories/args
+ *
+ * @type {{args: {label: string}, render: (function(*): TemplateResult<1>)}}
+ */
+export const Default = {
+ args: {
+ apiKey: 'ff04054b53589fc9f957a7dad05a3b4e',
+ user: 'priyanshu.rai@gmail.com',
+ model: '4908d8d4-9404-11ee-a242-0242ac110002',
+ input: 'What is Lorem Ipsum',
+ output:
+ 'The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.',
+ },
+
+ parameters: {
+ docs: { inlineStories: false, iframeHeight: 1200 },
+ },
+
+ /**
+ * Renders the template for Storybook
+ * @param {object} args Storybook arguments
+ * @returns {TemplateResult<1>}
+ */
+ render: (args) =>
+ html`
+
${args.input}?
+
+ ${args.output}
+
+ `,
+};
diff --git a/packages/chat/components/feedback/feedback-container.ts b/packages/chat/components/feedback/feedback-container.ts
new file mode 100644
index 00000000..21c101e6
--- /dev/null
+++ b/packages/chat/components/feedback/feedback-container.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { customElement } from 'lit/decorators.js';
+// need to set up package to import global files like the one below. Hardcoding
+//the `c4ai`prefixes for now
+
+// import settings from '../../globals/settings.js';
+import { FeedbackContainer } from './src/feedback-container.template.js';
+
+/**
+ * Component extending the @carbon/web-components' button
+ */
+@customElement(`c4ai-feedback-container`)
+class C4AIFeedbackContainer extends FeedbackContainer {}
+
+export default C4AIFeedbackContainer;
diff --git a/packages/chat/components/feedback/src/feedback-container.template.ts b/packages/chat/components/feedback/src/feedback-container.template.ts
new file mode 100644
index 00000000..df713295
--- /dev/null
+++ b/packages/chat/components/feedback/src/feedback-container.template.ts
@@ -0,0 +1,276 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { LitElement, html, css, PropertyValueMap } from 'lit';
+import { property, state } from 'lit/decorators.js';
+import { v4 as uuidv4 } from 'uuid';
+
+import '@carbon/web-components/es/components/modal/index.js';
+import '@carbon/web-components/es/components/textarea/index.js';
+import '@carbon/web-components/es/components/radio-button/index.js';
+import '@carbon/web-components/es/components/button/index.js';
+import '@carbon/web-components/es/components/checkbox/index.js';
+import '@carbon/web-components/es/components/form-group/index.js';
+
+import Flag24 from '@carbon/web-components/es/icons/flag/24';
+import { FeedbackApi } from '../../../services/feedback/api';
+
+export class FeedbackContainer extends LitElement {
+ static styles = css`
+ .carbon-feedback-wrapper {
+ position: relative;
+ }
+
+ .feedback_dialog {
+ position: absolute;
+ max-width: 300px;
+ border: 1px dashed lightcoral;
+ background: white;
+ color: black;
+ padding: 16px;
+ }
+
+ #selected-text > text-area {
+ background-color: red;
+ }
+
+ .bulb-icon {
+ position: absolute;
+ }
+ `;
+
+ @property({ attribute: 'api-key' })
+ private _api_key: string = '';
+
+ @property({ attribute: 'user' })
+ private _user_id: string = '';
+
+ @property({ attribute: 'ai-model' })
+ private _model_id: string = '';
+
+ @property({ attribute: 'input' })
+ private _input: string = '';
+
+ @property({ attribute: 'output' })
+ private _output: string = '';
+
+ @state()
+ generation_id: string = '';
+
+ @state()
+ app_id?: number;
+
+ @state()
+ private isModelOpen = false;
+
+ @state()
+ private selection;
+
+ // @state()
+ // private selectedText;
+
+ @state()
+ private formData = {
+ id: '',
+ generation_id: '',
+ start_index: 0,
+ end_index: 0,
+ selected_value: '',
+ corrected_value: '',
+ feedback: '',
+ comment: '',
+ };
+
+ private feedbackApi = FeedbackApi.getInstance();
+ private _feedbacks: string[] = [];
+
+ constructor() {
+ super();
+ this.generation_id = uuidv4();
+ }
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ if (
+ this._api_key &&
+ this._model_id &&
+ this._user_id &&
+ (this._input || this._output)
+ ) {
+ const payload = {
+ id: this.generation_id,
+ api_key: this._api_key,
+ model_id: this._model_id,
+ user_id: this._user_id,
+ input_value: this._input,
+ output_value: this._output,
+ };
+ this.feedbackApi.recordGeneration(payload);
+ }
+ }
+
+ handleTextSelection() {
+ let selection = window.getSelection()!;
+ this.selection = selection;
+ const selectedText = selection?.toString().trim();
+ // this.selectedText = selectedText;
+ this.setAttribute('selected', '');
+
+ if (selectedText) {
+ const minOffset = Math.min(selection.anchorOffset, selection.focusOffset);
+ const maxOffset = Math.max(selection.anchorOffset, selection.focusOffset);
+ this.formData.generation_id = this.generation_id;
+ this.formData.selected_value = selectedText;
+ this.formData.start_index = minOffset;
+ this.formData.end_index = maxOffset;
+ } else {
+ this.selection = null;
+ this.removeAttribute('selected');
+ }
+ }
+
+ bulb() {
+ const range = window.getSelection()?.getRangeAt(0).getBoundingClientRect();
+ return html`
+
+ ${Flag24({ slot: 'icon' })}
+
+ `;
+ }
+
+ handleTextInput(event) {
+ this.formData.corrected_value = event?.target.value;
+ }
+
+ handleTextArea(event) {
+ this.formData.comment = event?.target.value;
+ }
+
+ handleFeedRadio(event) {
+ this.formData.feedback = event?.detail.value;
+ }
+
+ handleFormData() {
+ if (!this.formData.corrected_value) {
+ this.formData.corrected_value = this.formData.selected_value;
+ }
+ this.feedbackApi.recordFeedback(this.formData);
+ // this.selectedText = '';
+ this.selection = '';
+ this.formData = {
+ id: '',
+ generation_id: '',
+ start_index: 0,
+ end_index: 0,
+ selected_value: '',
+ corrected_value: '',
+ feedback: '',
+ comment: '',
+ };
+ this.isModelOpen = false;
+ }
+
+ protected updated(
+ _changedProperties: PropertyValueMap | Map
+ ): void {
+ if (this.isModelOpen) {
+ this.removeEventListener('click', this.handleTextSelection, false);
+ } else {
+ this.addEventListener('click', this.handleTextSelection);
+ }
+ }
+
+ handleFeedback(e) {
+ let feedback = e.target.labelText;
+ if (!this._feedbacks.includes(feedback)) {
+ this._feedbacks.push(feedback);
+ } else {
+ this._feedbacks = this._feedbacks.filter((item) => item != feedback);
+ }
+ this.formData.feedback = this._feedbacks.join(' | ');
+ }
+
+ openModal() {
+ this.selection = null;
+ return html`
+ (this.isModelOpen = false)}>
+
+
+ Provide Feedback
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Comments:
+
+
+
+
+ Cancel
+ Save
+
+
+ `;
+ }
+
+ toggle() {
+ this.isModelOpen = !this.isModelOpen;
+ }
+
+ handleSlotChange() {}
+
+ render() {
+ return html`
+
+ ${this.selection ? this.bulb() : ''}
+
+ ${this.isModelOpen ? this.openModal() : ''}
+
+ `;
+ }
+}
diff --git a/packages/chat/components/test-input/test-input.ts b/packages/chat/components/test-input/test-input.ts
index c68dd992..966bda54 100644
--- a/packages/chat/components/test-input/test-input.ts
+++ b/packages/chat/components/test-input/test-input.ts
@@ -1,32 +1,32 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
+// /**
+// * @license
+// *
+// * Copyright IBM Corp. 2023
+// *
+// * This source code is licensed under the Apache-2.0 license found in the
+// * LICENSE file in the root directory of this source tree.
+// */
-import { customElement } from 'lit/decorators.js';
-import { settings } from '@carbon/ai-utilities/es/settings/index.js';
-import testInput from './src/test-input.js';
-import { testInputTemplate } from './src/test-input.template.js';
+// import { customElement } from 'lit/decorators.js';
+// import { settings } from '@carbon/ai-utilities/es/settings/index.js';
+// import testInput from './src/test-input.js';
+// import { testInputTemplate } from './src/test-input.template.js';
-const { stablePrefix: c4aiPrefix } = settings;
+// const { stablePrefix: c4aiPrefix } = settings;
-/**
- * Constructed class functionality for the test input custom element
- */
-@customElement(`${c4aiPrefix}-test-input`)
-class C4AITestInput extends testInput {
- /**
- * Renders the template while passing in class functionality
- *
- * @returns {TemplateResult<1>}
- */
- render() {
- return testInputTemplate(this);
- }
-}
+// /**
+// * Constructed class functionality for the test input custom element
+// */
+// @customElement(`${c4aiPrefix}-test-input`)
+// class C4AITestInput extends testInput {
+// /**
+// * Renders the template while passing in class functionality
+// *
+// * @returns {TemplateResult<1>}
+// */
+// render() {
+// return testInputTemplate(this);
+// }
+// }
-export default C4AITestInput;
+// export default C4AITestInput;
diff --git a/packages/chat/package.json b/packages/chat/package.json
index 51b04fec..f8c2c63c 100644
--- a/packages/chat/package.json
+++ b/packages/chat/package.json
@@ -35,6 +35,9 @@
"dependencies": {
"@babel/runtime": "^7.23.2",
"@carbon/ai-utilities": "0.0.1-rc.1",
- "lit": "^3.0.0"
+ "@carbon/styles": "^1.39.0",
+ "@carbon/web-components": "2.0.1-canary.1",
+ "lit": "^3.0.0",
+ "uuid": "^9.0.1"
}
}
diff --git a/packages/chat/services/feedback/api.ts b/packages/chat/services/feedback/api.ts
new file mode 100644
index 00000000..092dc8aa
--- /dev/null
+++ b/packages/chat/services/feedback/api.ts
@@ -0,0 +1,46 @@
+export class FeedbackApi {
+ private static instance: FeedbackApi;
+ private BASE_URL: string;
+
+ constructor() {
+ this.BASE_URL = 'http://0.0.0.0:8000';
+ }
+
+ protected async _post(endpoint: string, data) {
+ const response = await fetch(`${this.BASE_URL}/${endpoint}`, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ headers: {
+ 'Content-type': 'application/json; charset=UTF-8',
+ },
+ });
+ return this._handleResponse(response);
+ }
+
+ protected async _handleResponse(response) {
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(
+ errorData?.detail || errorData?.message || 'Something went wrong'
+ );
+ }
+ return await response.json();
+ }
+
+ async recordGeneration(data) {
+ const response = await this._post('generated_content', data);
+ return response;
+ }
+
+ async recordFeedback(data) {
+ const response = await this._post('feedback', data);
+ return response;
+ }
+
+ public static getInstance(): FeedbackApi {
+ if (!FeedbackApi.instance) {
+ FeedbackApi.instance = new FeedbackApi();
+ }
+ return FeedbackApi.instance;
+ }
+}
diff --git a/packages/extended-button/components/extended-button/extended-button.ts b/packages/extended-button/components/extended-button/extended-button.ts
index f6bd04dc..c2637e81 100644
--- a/packages/extended-button/components/extended-button/extended-button.ts
+++ b/packages/extended-button/components/extended-button/extended-button.ts
@@ -1,22 +1,22 @@
-/**
- * @license
- *
- * Copyright IBM Corp. 2023
- *
- * This source code is licensed under the Apache-2.0 license found in the
- * LICENSE file in the root directory of this source tree.
- */
+// /**
+// * @license
+// *
+// * Copyright IBM Corp. 2023
+// *
+// * This source code is licensed under the Apache-2.0 license found in the
+// * LICENSE file in the root directory of this source tree.
+// */
-import { customElement } from 'lit/decorators.js';
-import { settings } from '@carbon/ai-utilities/es/settings/index.js';
-import extendedButton from './src/extended-button.template.js';
+// import { customElement } from 'lit/decorators.js';
+// import { settings } from '@carbon/ai-utilities/es/settings/index.js';
+// import extendedButton from './src/extended-button.template.js';
-const { stablePrefix: c4aiPrefix } = settings;
+// const { stablePrefix: c4aiPrefix } = settings;
-/**
- * Component extending the @carbon/web-components' button
- */
-@customElement(`${c4aiPrefix}-extended-button`)
-class C4AIExtendedButton extends extendedButton {}
+// /**
+// * Component extending the @carbon/web-components' button
+// */
+// @customElement(`${c4aiPrefix}-extended-button`)
+// class C4AIExtendedButton extends extendedButton {}
-export default C4AIExtendedButton;
+// export default C4AIExtendedButton;
diff --git a/packages/feedback/__stories__/feedback-container.stories.js b/packages/feedback/__stories__/feedback-container.stories.js
new file mode 100644
index 00000000..8fd93075
--- /dev/null
+++ b/packages/feedback/__stories__/feedback-container.stories.js
@@ -0,0 +1,63 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import '../components/feedback-container/feedback-container';
+import '../components/input-container/input-container';
+import '../components/output-container/output-container';
+
+import { html } from 'lit';
+/**
+ * More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
+ */
+export default {
+ title: 'Components/Feedback container/Feedback container',
+ tags: ['autodocs'],
+};
+
+/**
+ * More on writing stories with args: https://storybook.js.org/docs/web-components/writing-stories/args
+ *
+ * @type {{args: {label: string}, render: (function(*): TemplateResult<1>)}}
+ */
+export const Default = {
+ args: {
+ apiKey: 'api-key',
+ user: 'user id',
+ model: 'model name',
+ },
+
+ /**
+ * Renders the template for Storybook
+ * @param {object} args Storybook arguments
+ * @returns {TemplateResult<1>}
+ */
+ render: (args) =>
+ html`
+
+
+ Hello, How can i help you today
+
+
+ Hello! how are you?
+
+
+ I'm just a computer program, but thanks for asking!
+
+
+ How's weather today outside?
+
+
+ It's 15.0 degree celcius today
+
+
+ `,
+};
diff --git a/packages/feedback/components/feedback-container/feedback-container.ts b/packages/feedback/components/feedback-container/feedback-container.ts
new file mode 100644
index 00000000..21c101e6
--- /dev/null
+++ b/packages/feedback/components/feedback-container/feedback-container.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { customElement } from 'lit/decorators.js';
+// need to set up package to import global files like the one below. Hardcoding
+//the `c4ai`prefixes for now
+
+// import settings from '../../globals/settings.js';
+import { FeedbackContainer } from './src/feedback-container.template.js';
+
+/**
+ * Component extending the @carbon/web-components' button
+ */
+@customElement(`c4ai-feedback-container`)
+class C4AIFeedbackContainer extends FeedbackContainer {}
+
+export default C4AIFeedbackContainer;
diff --git a/packages/feedback/components/feedback-container/src/feedback-container.template.ts b/packages/feedback/components/feedback-container/src/feedback-container.template.ts
new file mode 100644
index 00000000..0fcd3c09
--- /dev/null
+++ b/packages/feedback/components/feedback-container/src/feedback-container.template.ts
@@ -0,0 +1,75 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { LitElement, html, css } from 'lit';
+import { property, state } from 'lit/decorators.js';
+
+import '@carbon/web-components/es/components/textarea/index.js';
+import '@carbon/web-components/es/components/radio-button/index.js';
+import '@carbon/web-components/es/components/button/index.js';
+
+export class FeedbackContainer extends LitElement {
+ static styles = css`
+ .carbon-feedback-wrapper {
+ position: relative;
+ }
+
+ .feedback_dialog {
+ position: absolute;
+ max-width: 300px;
+ border: 1px dashed lightcoral;
+ background: white;
+ color: black;
+ padding: 16px;
+ }
+
+ #selected-text > text-area {
+ background-color: red;
+ }
+
+ .bulb-icon {
+ position: absolute;
+ }
+ `;
+
+ @property({ attribute: 'api-key' })
+ private _api_key: string = '';
+
+ @property({ attribute: 'user' })
+ private _user_id: string = '';
+
+ @property({ attribute: 'ai-model' })
+ private _model_id: string = '';
+
+ get api_key() {
+ return this._api_key;
+ }
+
+ get user_id() {
+ return this._user_id;
+ }
+
+ get model_id() {
+ return this._model_id;
+ }
+
+ @state()
+ generation_id?: string = '';
+
+ @state()
+ app_id?: number;
+
+ render() {
+ return html`
+
+
+
+ `;
+ }
+}
diff --git a/packages/feedback/components/input-container/input-container.ts b/packages/feedback/components/input-container/input-container.ts
new file mode 100644
index 00000000..9021cbc6
--- /dev/null
+++ b/packages/feedback/components/input-container/input-container.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { customElement } from 'lit/decorators.js';
+// need to set up package to import global files like the one below. Hardcoding
+//the `c4ai`prefixes for now
+
+// import settings from '../../globals/settings.js';
+import { InputContainer } from './src/input-container.template.js';
+
+/**
+ * Component extending the @carbon/web-components' button
+ */
+@customElement(`c4ai-input-container`)
+class C4AIInputContainer extends InputContainer {}
+
+export default C4AIInputContainer;
diff --git a/packages/feedback/components/input-container/src/input-container.template.ts b/packages/feedback/components/input-container/src/input-container.template.ts
new file mode 100644
index 00000000..bb7c2a05
--- /dev/null
+++ b/packages/feedback/components/input-container/src/input-container.template.ts
@@ -0,0 +1,79 @@
+import { LitElement, html } from 'lit';
+import { property } from 'lit/decorators.js';
+import { v4 as uuidv4 } from 'uuid';
+import { FeedbackContainer } from '../../feedback-container/src/feedback-container.template';
+import { FeedbackApi } from '../../../services/api';
+
+export class InputContainer extends LitElement {
+ @property({ type: String })
+ generationId: string = '';
+
+ @property({ type: String })
+ content: string = '';
+
+ feedbackApi = FeedbackApi.getInstance();
+
+ constructor() {
+ super();
+ if (!this.generationId) {
+ this.generationId = uuidv4();
+ }
+ this.setAttribute('generation_id', this.generationId);
+ }
+
+ get _slottedChildren() {
+ const slot = this.shadowRoot?.querySelector('slot')!;
+ return slot.assignedElements({ flatten: true });
+ }
+
+ handleSlotchange() {
+ // const text = this._slottedChildren[0].innerHTML;
+ // this.content = text;
+
+ const slot = this.shadowRoot?.querySelector('slot')!;
+ const assignNodes = slot.assignedNodes();
+ assignNodes.forEach((node) => {
+ if (
+ node.nodeType === Node.ELEMENT_NODE &&
+ node.textContent?.trim() !== ''
+ ) {
+ const text = node.textContent?.trim();
+ this.content = text!;
+ } else if (node.textContent?.trim() !== '') {
+ this.content = node.textContent?.trim()!;
+ }
+ });
+ }
+
+ updated(changedProperties) {
+ super.updated(changedProperties);
+ const feedbackContainer = this.parentElement as FeedbackContainer;
+ if (
+ feedbackContainer &&
+ feedbackContainer.api_key &&
+ feedbackContainer.model_id &&
+ feedbackContainer.user_id &&
+ this.content
+ ) {
+ const payload = {
+ id: this.generationId,
+ api_key: feedbackContainer.api_key,
+ model_id: feedbackContainer.model_id,
+ user_id: feedbackContainer.user_id,
+ input_value: this.content,
+ };
+
+ this.feedbackApi.recordGeneration(payload);
+ // feedbackContainer.recordGeneration(payload)
+ }
+ }
+
+ render() {
+ return html`
+
+ User:
+
+ `;
+ }
+}
diff --git a/packages/feedback/components/output-container/output-container.ts b/packages/feedback/components/output-container/output-container.ts
new file mode 100644
index 00000000..1b1ef77f
--- /dev/null
+++ b/packages/feedback/components/output-container/output-container.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { customElement } from 'lit/decorators.js';
+// need to set up package to import global files like the one below. Hardcoding
+//the `c4ai`prefixes for now
+
+// import settings from '../../globals/settings.js';
+import { OutputContainer } from './src/output-container.template.js';
+
+/**
+ * Component extending the @carbon/web-components' button
+ */
+@customElement(`c4ai-output-container`)
+class C4AIOutputContainer extends OutputContainer {}
+
+export default C4AIOutputContainer;
diff --git a/packages/feedback/components/output-container/src/output-container.template.ts b/packages/feedback/components/output-container/src/output-container.template.ts
new file mode 100644
index 00000000..96a01c19
--- /dev/null
+++ b/packages/feedback/components/output-container/src/output-container.template.ts
@@ -0,0 +1,226 @@
+import { property, state } from 'lit/decorators.js';
+import { LitElement, html } from 'lit';
+import { FeedbackContainer } from '../../feedback-container/src/feedback-container.template';
+import { InputContainer } from '../../input-container/src/input-container.template';
+import { FeedbackApi } from '../../../services/api';
+import Flag16 from '@carbon/web-components/es/icons/flag/24';
+import { v4 as uuidv4 } from 'uuid';
+export class OutputContainer extends LitElement {
+ @property()
+ content = '';
+
+ @property({ type: String })
+ generationId = '';
+
+ @state()
+ isModelOpen = false;
+
+ @state()
+ private selection;
+
+ @state()
+ private selectedText;
+
+ @state()
+ private formData = {
+ id: '',
+ generation_id: '',
+ start_index: 0,
+ end_index: 0,
+ selected_value: '',
+ corrected_value: '',
+ feedback: '',
+ comment: '',
+ };
+
+ feedbackApi = FeedbackApi.getInstance();
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ this.addEventListener('click', this.handleTextSelection);
+ }
+
+ shouldUpdate(changedProperties) {
+ super.shouldUpdate(changedProperties);
+ const previousInputContainer = this
+ .previousElementSibling as InputContainer;
+
+ this.generationId = previousInputContainer?.generationId;
+ if (this.generationId) {
+ this.setAttribute('generation-id', this.generationId);
+ } else {
+ this.generationId = uuidv4();
+ this.setAttribute('generation-id', this.generationId);
+ }
+ return true;
+ }
+
+ updated(changedProperties) {
+ super.updated(changedProperties);
+
+ const feedbackContainer = this.parentElement as FeedbackContainer;
+ if (
+ feedbackContainer &&
+ feedbackContainer.api_key &&
+ feedbackContainer.model_id &&
+ feedbackContainer.user_id &&
+ this.content &&
+ changedProperties.has('content')
+ ) {
+ const payload = {
+ id: this.generationId,
+ api_key: feedbackContainer.api_key,
+ model_id: feedbackContainer.model_id,
+ user_id: feedbackContainer.user_id,
+ output_value: this.content,
+ };
+ this.feedbackApi.recordGeneration(payload);
+ }
+ }
+
+ get _slottedChildren() {
+ const slot = this.shadowRoot?.querySelector('slot')!;
+ return slot.assignedElements({ flatten: true });
+ }
+
+ handleTextSelection() {
+ console.log('Output: ', this.generationId);
+ let selection = window.getSelection()!;
+ this.selection = selection;
+ const selectedText = selection?.toString().trim();
+ this.selectedText = selectedText;
+ this.setAttribute('selected', '');
+
+ if (selectedText) {
+ const minOffset = Math.min(selection.anchorOffset, selection.focusOffset);
+ const maxOffset = Math.max(selection.anchorOffset, selection.focusOffset);
+ this.formData.generation_id = this.generationId;
+ this.formData.selected_value = selectedText;
+ this.formData.start_index = minOffset;
+ this.formData.end_index = maxOffset;
+ } else {
+ this.selection = null;
+ this.removeAttribute('selected');
+ }
+ }
+
+ handleSlotchange() {
+ // const text = this._slottedChildren[0]?.innerHTML;
+ // this.content = text;
+
+ const slot = this.shadowRoot?.querySelector('slot')!;
+ const assignNodes = slot.assignedNodes();
+ assignNodes.forEach((node) => {
+ if (
+ node.nodeType === Node.ELEMENT_NODE &&
+ node.textContent?.trim() !== ''
+ ) {
+ const text = node.textContent?.trim();
+ this.content = text!;
+ } else if (node.textContent?.trim() !== '') {
+ this.content = node.textContent?.trim()!;
+ }
+ });
+ }
+
+ bulb() {
+ const range = window.getSelection()?.getRangeAt(0).getBoundingClientRect();
+ return html`
+
+ ${Flag16({ slot: 'icon' })}
+
+ `;
+ }
+
+ handleTextArea(event) {
+ this.formData.corrected_value = event?.target.value;
+ }
+
+ handleFeedRadio(event) {
+ this.formData.feedback = event?.detail.value;
+ }
+
+ handleFormData() {
+ if (!this.formData.corrected_value) {
+ this.formData.corrected_value = this.formData.selected_value;
+ }
+ this.feedbackApi.recordFeedback(this.formData);
+ this.selectedText = '';
+ this.selection = '';
+ this.formData = {
+ id: '',
+ generation_id: '',
+ start_index: 0,
+ end_index: 0,
+ selected_value: '',
+ corrected_value: '',
+ feedback: '',
+ comment: '',
+ };
+ this.isModelOpen = false;
+ }
+
+ openModal() {
+ const range = window.getSelection()?.getRangeAt(0).getBoundingClientRect();
+ return html`
+
+
+ Selected Text:
+
+
+
+
+
+
+
+
+ Save
+
+
+ `;
+ }
+
+ toggle() {
+ this.isModelOpen = !this.isModelOpen;
+ }
+
+ render() {
+ return html`
+
+
Bot:
+
+ ${this.selection ? this.bulb() : ''}
+
+ ${this.isModelOpen ? this.openModal() : ''}
+
+
+ `;
+ }
+}
diff --git a/packages/feedback/index.ts b/packages/feedback/index.ts
new file mode 100644
index 00000000..32916fc7
--- /dev/null
+++ b/packages/feedback/index.ts
@@ -0,0 +1,10 @@
+/**
+ * @license
+ *
+ * Copyright IBM Corp. 2023
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import './feedback-container/feedback-container.js';
diff --git a/packages/feedback/package.json b/packages/feedback/package.json
new file mode 100644
index 00000000..d40a0e1a
--- /dev/null
+++ b/packages/feedback/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@carbon-labs/feedback",
+ "version": "0.0.1",
+ "publishConfig": {
+ "access": "public"
+ },
+ "description": "Carbon for AI - feedback component",
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/carbon-design-system/carbon-for-ai",
+ "directory": "packages/feedback"
+ },
+ "main": "./src/index.js",
+ "module": "./src/index.js",
+ "exports": {
+ ".": {
+ "default": "./src/index.js"
+ },
+ "./es/": "./es/"
+ },
+ "files": [
+ "**/*.d.ts",
+ "**/*.js",
+ "**/*.js.map",
+ "custom-elements.json"
+ ],
+ "types": "./src/index.d.ts",
+ "customElements": "custom-elements.json",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@carbon/styles": "^1.39.0",
+ "@carbon/web-components": "2.0.1-canary.1",
+ "lit": "^3.0.0",
+ "uuid": "^9.0.1"
+ }
+}
diff --git a/packages/feedback/services/api.ts b/packages/feedback/services/api.ts
new file mode 100644
index 00000000..092dc8aa
--- /dev/null
+++ b/packages/feedback/services/api.ts
@@ -0,0 +1,46 @@
+export class FeedbackApi {
+ private static instance: FeedbackApi;
+ private BASE_URL: string;
+
+ constructor() {
+ this.BASE_URL = 'http://0.0.0.0:8000';
+ }
+
+ protected async _post(endpoint: string, data) {
+ const response = await fetch(`${this.BASE_URL}/${endpoint}`, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ headers: {
+ 'Content-type': 'application/json; charset=UTF-8',
+ },
+ });
+ return this._handleResponse(response);
+ }
+
+ protected async _handleResponse(response) {
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(
+ errorData?.detail || errorData?.message || 'Something went wrong'
+ );
+ }
+ return await response.json();
+ }
+
+ async recordGeneration(data) {
+ const response = await this._post('generated_content', data);
+ return response;
+ }
+
+ async recordFeedback(data) {
+ const response = await this._post('feedback', data);
+ return response;
+ }
+
+ public static getInstance(): FeedbackApi {
+ if (!FeedbackApi.instance) {
+ FeedbackApi.instance = new FeedbackApi();
+ }
+ return FeedbackApi.instance;
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7867b48f..01c66b0c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -252,9 +252,18 @@ importers:
'@carbon/ai-utilities':
specifier: 0.0.1-rc.1
version: link:../utilities
+ '@carbon/styles':
+ specifier: ^1.39.0
+ version: 1.42.0(sass@1.69.5)
+ '@carbon/web-components':
+ specifier: 2.0.1-canary.1
+ version: 2.0.1-canary.1(sass@1.69.5)
lit:
specifier: ^3.0.0
version: 3.0.2
+ uuid:
+ specifier: ^9.0.1
+ version: 9.0.1
packages/extended-button:
dependencies:
@@ -274,6 +283,24 @@ importers:
specifier: ^3.0.0
version: 3.0.2
+ packages/feedback:
+ dependencies:
+ '@babel/runtime':
+ specifier: ^7.23.2
+ version: 7.23.2
+ '@carbon/styles':
+ specifier: ^1.39.0
+ version: 1.42.0(sass@1.69.5)
+ '@carbon/web-components':
+ specifier: 2.0.1-canary.1
+ version: 2.0.1-canary.1(sass@1.69.5)
+ lit:
+ specifier: ^3.0.0
+ version: 3.0.2
+ uuid:
+ specifier: ^9.0.1
+ version: 9.0.1
+
packages/utilities:
devDependencies:
'@rollup/plugin-babel':
@@ -19301,7 +19328,6 @@ packages:
/uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
- dev: true
/v8-compile-cache@2.3.0:
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}