Skip to content

Commit 7933c9f

Browse files
authored
Merge pull request #22 from primeqa/lfqa-demo
Lfqa demo
2 parents eda286a + 7ffcc79 commit 7933c9f

File tree

6 files changed

+255
-75
lines changed

6 files changed

+255
-75
lines changed

src/applications/qa/index.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,21 @@ async function ask(
5454
let answers = [];
5555
if (resp && !_.isEmpty(resp)) {
5656
// Step 2.a: Iterate over each entry in response to build answer's object
57-
resp.forEach((entry) => {
58-
answers.push({
59-
text: entry.answer.text,
60-
context: entry.document.text,
61-
startCharOffset: entry.answer.start_char_offset,
62-
endCharOffset: entry.answer.end_char_offset,
63-
title: entry.document.title,
64-
url: entry.document.url,
65-
confidenceScore: entry.answer.confidence_score,
57+
if (resp.answers) {
58+
resp.answers.forEach((entry) => {
59+
// Step 2.a.i: Mandator fields
60+
let answer = {
61+
text: entry.text,
62+
confidenceScore: entry.confidence_score,
63+
};
64+
65+
// Step 2.a.ii: Optional fields
66+
if (entry.evidences && !_.isEmpty(entry.evidences)) {
67+
answer.evidences = entry.evidences;
68+
}
69+
answers.push(answer);
6670
});
67-
});
71+
}
6872
}
6973

7074
setAnswers(answers);
@@ -136,8 +140,9 @@ function QuestionAnswering({ application, showSettings }) {
136140
</div>
137141
<div className="application__content">
138142
<h4>What would you like to know?</h4>
139-
<Form className="qa__input"
140-
onSubmit={evt => {
143+
<Form
144+
className="qa__input"
145+
onSubmit={(evt) => {
141146
evt.preventDefault();
142147

143148
// Step 1: Set processing to true

src/applications/reading/index.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,18 @@ async function read(
5050
// Step 2: Verify response came with "text" response type
5151
let answers = [];
5252
if (resp && !_.isEmpty(resp)) {
53-
resp.forEach((answer) => {
54-
answers.push({
55-
text: answer.text,
56-
context: context,
57-
startCharOffset: answer.start_char_offset,
58-
endCharOffset: answer.end_char_offset,
59-
confidenceScore: answer.confidence_score,
60-
});
53+
resp.forEach((entry) => {
54+
// Mandatory fields
55+
let answer = {
56+
text: entry.text,
57+
confidenceScore: entry.confidence_score,
58+
};
59+
60+
// Optional fields
61+
if (entry.evidences && !_.isEmpty(entry.evidences)) {
62+
answer.evidences = entry.evidences;
63+
}
64+
answers.push(answer);
6165
});
6266
}
6367

@@ -140,8 +144,9 @@ function Reading({ application, showSettings }) {
140144
setContext(event.target.value);
141145
}}
142146
></TextArea>
143-
<Form className="reading__input--box"
144-
onSubmit={evt => {
147+
<Form
148+
className="reading__input--box"
149+
onSubmit={(evt) => {
145150
evt.preventDefault();
146151

147152
// Step 1: Set processing to true

src/components/answers/index.js

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
} from "@carbon/react/icons";
3737

3838
import { generateUUID } from "../../util/uuid";
39+
import Evidence from "../evidence";
3940
import {
4041
getFeedbacks as getFeedbacksAPI,
4142
postFeedback as postFeedbackAPI,
@@ -58,17 +59,47 @@ async function buildAnswersWithFeedback(
5859
// Step 1.a: Iterate over each answer
5960
answers.forEach((answer, answerIndex) => {
6061
// Step 1.a.i: Generate feedback_id
61-
const feedback_id = generateUUID(
62-
question +
63-
"::" +
64-
answer.context.replace(/\s/g, "") +
65-
"::" +
66-
answer.text.replace(/\s/g, "") +
67-
"::" +
68-
answer.startCharOffset +
69-
"::" +
70-
answer.endCharOffset
71-
);
62+
// FIXME: Need to come up with improved logic to generate unique feedback ID based on evidences
63+
let feedback_id = null;
64+
if (answer.evidences) {
65+
let evidenceContent = "";
66+
answer.evidences.forEach((evidence) => {
67+
// Step 1.a.i.*: If evidence is of "text" type, use "text" field
68+
if (evidence.evidence_type === "text") {
69+
evidenceContent += evidence.text.replace(/\s/g, "") + ":::";
70+
}
71+
// Step 1.a.i.**: If evidence is of "document" type, use "document_id" field if present (fall back on "text" field)
72+
else if (evidence.evidence_type === "document") {
73+
if (evidence.document_id) {
74+
evidenceContent += evidence.document_id.replace(/\s/g, "") + ":::";
75+
} else {
76+
evidenceContent += evidence.text.replace(/\s/g, "") + ":::";
77+
}
78+
}
79+
80+
// Step 1.a.i.***: Add offsets information, if present
81+
if (evidence.offsets) {
82+
let offsetsContent = "[";
83+
evidence.offsets.forEach((offset) => {
84+
offsetsContent += "(" + offset.start + "," + offset.end + "), ";
85+
});
86+
evidenceContent += offsetsContent.slice(0, -2) + "]:::";
87+
}
88+
});
89+
90+
// Use question, answer's text and answer's evidence information
91+
feedback_id = generateUUID(
92+
question +
93+
"::" +
94+
answer.text.replace(/\s/g, "") +
95+
"::" +
96+
evidenceContent.slice(0, -3)
97+
);
98+
} else {
99+
feedback_id = generateUUID(
100+
question + "::" + answer.text.replace(/\s/g, "")
101+
);
102+
}
72103

73104
// Step 1.a.ii: Update feedbackIdToAnswerIndexMap
74105
feedbackIdToAnswerIndexMap.set(feedback_id, answerIndex);
@@ -292,30 +323,31 @@ function Answers({ question, answers, loading, source }) {
292323
<div className="answers--item__content">
293324
<div className="answers--item__content--left">
294325
<span>{answerWithFeedback.text}</span>
295-
{Object.hasOwn(answerWithFeedback, "context") &&
296-
answerWithFeedback.context ? (
326+
{Object.hasOwn(answerWithFeedback, "evidences") &&
327+
answerWithFeedback.evidences ? (
297328
<Accordion
298329
align="start"
299330
size="md"
300-
className="answers--item__context"
331+
className="answers--item__evidence"
301332
>
302-
<AccordionItem title="Evidence">
303-
<p>
304-
{answerWithFeedback.context.slice(
305-
0,
306-
answerWithFeedback.startCharOffset
307-
)}
308-
<b className="answers--item__context--highlight">
309-
{answerWithFeedback.context.slice(
310-
answerWithFeedback.startCharOffset,
311-
answerWithFeedback.endCharOffset
312-
)}
313-
</b>
314-
{answerWithFeedback.context.slice(
315-
answerWithFeedback.endCharOffset
316-
)}
317-
</p>
318-
</AccordionItem>
333+
{answerWithFeedback.evidences.length > 1 ? (
334+
answerWithFeedback.evidences.map(
335+
(evidence, evidence_index) => (
336+
<AccordionItem
337+
title={"Evidence " + (evidence_index + 1)}
338+
key={"Evidence " + (evidence_index + 1)}
339+
>
340+
<Evidence evidence={evidence}></Evidence>
341+
</AccordionItem>
342+
)
343+
)
344+
) : (
345+
<AccordionItem title={"Evidence"} key={"Evidence"}>
346+
<Evidence
347+
evidence={answerWithFeedback.evidences[0]}
348+
></Evidence>
349+
</AccordionItem>
350+
)}
319351
</Accordion>
320352
) : null}
321353
</div>

src/components/answers/styles.scss

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -117,27 +117,5 @@
117117
display: flex;
118118
align-items: center;
119119
}
120-
121-
&__context {
122-
padding-top: 1%;
123-
124-
&--highlight {
125-
font-weight: 600;
126-
background-color: $blue-20;
127-
}
128-
129-
.cds--accordion__item {
130-
border-top: none;
131-
margin-top: 10px;
132-
133-
&:last-child {
134-
border-bottom: none;
135-
}
136-
137-
.cds--accordion__heading {
138-
color: $blue-60;
139-
}
140-
}
141-
}
142120
}
143121
}

src/components/evidence/index.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
*
3+
* Copyright 2022 PrimeQA Team
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
import React from "react";
20+
import PropTypes from "prop-types";
21+
import _ from "lodash";
22+
23+
import "./styles.scss";
24+
25+
function renderText(evidence) {
26+
return (
27+
<p className="answers--item__evidence-text">
28+
{!_.isNil(evidence.offsets) && !_.isEmpty(evidence.offsets) ? (
29+
<React.Fragment>
30+
{evidence.offsets.map((offset, offset_index) => {
31+
return offset_index === 0 ? (
32+
<React.Fragment
33+
key={"answers--item__evidence--offset-" + offset_index}
34+
>
35+
{evidence.text.slice(0, offset.start)}
36+
<b className="answers--item__evidence--highlight">
37+
{evidence.text.slice(offset.start, offset.end)}
38+
</b>
39+
</React.Fragment>
40+
) : (
41+
<React.Fragment
42+
key={"answers--item__evidence--offset-" + offset_index}
43+
>
44+
{evidence.text.slice(
45+
evidence.offsets[offset_index - 1].end,
46+
offset.start
47+
)}
48+
<b className="answers--item__evidence--highlight">
49+
{evidence.text.slice(offset.start, offset.end)}
50+
</b>
51+
</React.Fragment>
52+
);
53+
})}
54+
{evidence.text.slice(
55+
evidence.offsets[evidence.offsets.length - 1].end
56+
)}
57+
</React.Fragment>
58+
) : (
59+
evidence.text
60+
)}
61+
</p>
62+
);
63+
}
64+
65+
function renderDocumentEvidence(evidence) {
66+
return (
67+
<div className="answers--item__evidence--document">
68+
{evidence.title ? (
69+
<React.Fragment>
70+
<div className="answers--item__evidence--document-title">
71+
<strong>Title:</strong>
72+
<span>{evidence.title}</span>
73+
</div>
74+
<div className="answers--item__evidence--document-text">
75+
<strong>Text:</strong> {renderText(evidence)}
76+
</div>
77+
</React.Fragment>
78+
) : (
79+
renderText(evidence)
80+
)}
81+
</div>
82+
);
83+
}
84+
85+
function Evidence({ evidence }) {
86+
return (
87+
<React.Fragment>
88+
{evidence.evidence_type === "document"
89+
? renderDocumentEvidence(evidence)
90+
: renderText(evidence)}
91+
</React.Fragment>
92+
);
93+
}
94+
95+
Evidence.propTypes = {
96+
evidence: PropTypes.object,
97+
};
98+
99+
export default Evidence;

src/components/evidence/styles.scss

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
*
3+
* Copyright 2022 PrimeQA Team
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
@use "@carbon/react/scss/spacing" as *;
20+
@use "@carbon/react/scss/colors" as *;
21+
22+
.answers--item__evidence {
23+
padding-top: 1%;
24+
25+
&--document {
26+
display: flex;
27+
flex-direction: column;
28+
29+
&-title {
30+
padding-top: 1%;
31+
display: flex;
32+
align-items: baseline;
33+
gap: 1%;
34+
}
35+
36+
&-text {
37+
padding-top: 1%;
38+
display: flex;
39+
align-items: baseline;
40+
gap: 1%;
41+
}
42+
}
43+
44+
&--highlight {
45+
font-weight: 600;
46+
background-color: $blue-20;
47+
}
48+
49+
.cds--accordion__item {
50+
border-top: none;
51+
margin-top: 10px;
52+
53+
&:last-child {
54+
border-bottom: none;
55+
}
56+
57+
.cds--accordion__heading {
58+
color: $blue-60;
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)