194 lines
11 KiB
TypeScript
194 lines
11 KiB
TypeScript
import Image from "next/image";
|
|
import clsx from "clsx";
|
|
import RadialProgressBar from "./RadialProgressBar";
|
|
import { AIDetectionAttributes } from "@/interfaces/exam";
|
|
import { Tooltip } from 'react-tooltip';
|
|
import SegmentedProgressBar from "./SegmentedProgressBar";
|
|
|
|
|
|
// Colors and texts scrapped from gpt's zero react bundle
|
|
const AIDetection: React.FC<AIDetectionAttributes> = ({ predicted_class, confidence_category, class_probabilities, sentences }) => {
|
|
const probabilityTooltipContent = `
|
|
Encoach's deep learning model predicts the <br/>
|
|
probability this text has been entirely <br/>
|
|
generated by AI. For instance, a 40% AI <br/>
|
|
probability does not indicate that the text<br/>
|
|
contains 40% AI-written content. Rather, it<br/>
|
|
indicates the text is more likely to be partially<br/>
|
|
human written than be entirely AI-written.
|
|
`;
|
|
const confidenceTooltipContent = `
|
|
Confidence scores are a safeguard to better<br/>
|
|
understand AI identification results. Encoach<br/>
|
|
trained it's deep learning model on a diverse<br/>
|
|
dataset of millions of human and AI-written<br/>
|
|
documents. Green scores indicate that you can scan<br/>
|
|
with confidence that the model has classified<br/>
|
|
many similar documents with high accuracy.<br/>
|
|
Red scores indicate that this text is dissimilar<br/>
|
|
to the ones in their training set, which can impact<br/>
|
|
the model's accuracy, and to proceed with caution.
|
|
`;
|
|
const confidenceKeywords = ["moderately", "highly", "confident", "uncertain"];
|
|
var confidence = {
|
|
low: {
|
|
ai: "Encoach is uncertain about this text. If Encoach had to classify it, it would be considered",
|
|
human: "Encoach is uncertain about this text. If Encoach had to classify it, it would likely be considered",
|
|
mixed: "Encoach is uncertain about this text. If Encoach had to classify it, it would likely be a"
|
|
},
|
|
medium: {
|
|
ai: "Encoach is moderately confident this text was",
|
|
human: "Encoach is moderately confident this text is entirely",
|
|
mixed: "Encoach is moderately confident this text is a"
|
|
},
|
|
high: {
|
|
ai: "Encoach is highly confident this text was",
|
|
human: "Encoach is highly confident this text is entirely",
|
|
mixed: "Encoach is highly confident this text is a"
|
|
}
|
|
}
|
|
var classPrediction = {
|
|
ai: {
|
|
background: "bg-ai-detection-result-ai-bg",
|
|
color: "text-ai-detection-result-ai",
|
|
text: "ai generated"
|
|
},
|
|
mixed: {
|
|
background: "bg-ai-detection-result-mixed-bg",
|
|
color: "text-ai-detection-result-mixed",
|
|
text: "mix of ai and human"
|
|
},
|
|
human: {
|
|
background: "bg-ai-detection-result-human-bg",
|
|
color: "text-ai-detection-result-human",
|
|
text: "human"
|
|
}
|
|
}
|
|
const segments = [
|
|
{ percentage: Math.round(class_probabilities["human"] * 100), subtitle: 'human', color: "ai-detection-result-human" },
|
|
{ percentage: Math.round(class_probabilities["mixed"] * 100), subtitle: 'mixed', color: "ai-detection-result-mixed" },
|
|
{ percentage: Math.round(class_probabilities["ai"] * 100), subtitle: 'ai', color: "ai-detection-result-ai" }
|
|
];
|
|
const styleConfidenceText = (text: string): [string, string[]] => {
|
|
const keywords: string[] = [];
|
|
const styledText = text.split(" ").map(word => {
|
|
if (confidenceKeywords.includes(word)) {
|
|
keywords.push(word);
|
|
return `<span style="font-weight: 500; text-decoration: underline;">${word}</span>`;
|
|
}
|
|
return word
|
|
}).join(" ");
|
|
return [styledText, keywords];
|
|
};
|
|
const confidenceText = confidence[confidence_category][predicted_class];
|
|
const [styledText, keywords] = styleConfidenceText(confidenceText);
|
|
const tooltipStyle = {
|
|
"backgroundColor": "rgb(255, 255, 255)",
|
|
"color": "#8992B1",
|
|
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
borderRadius: '0.125rem'
|
|
}
|
|
const highestProbability = Math.max(class_probabilities["ai"], class_probabilities["human"], class_probabilities["mixed"]);
|
|
const spanTextColor = highestProbability === class_probabilities["ai"]
|
|
? "#f4bf4f"
|
|
: highestProbability === class_probabilities["human"]
|
|
? "#50c08a"
|
|
: "#93aafb";
|
|
let spanClassName = highestProbability === class_probabilities["ai"]
|
|
? "text-ai-detection-result-ai"
|
|
: highestProbability === class_probabilities["human"]
|
|
? "text-ai-detection-result-human"
|
|
: "text-ai-detection-result-mixed";
|
|
spanClassName = `${spanClassName} font-bold text-lg`
|
|
const percentage = Math.round(highestProbability * 100)
|
|
const hasHighlightedForAI = sentences.some(item => item.highlight_sentence_for_ai);
|
|
return (
|
|
<>
|
|
<Tooltip id="probability-tooltip" className="z-50 bg-white shadow-md rounded-sm" style={tooltipStyle} />
|
|
<Tooltip id="confidence-tooltip" className="z-50 bg-white shadow-md rounded-sm" style={tooltipStyle} />
|
|
<div className="flex flex-col bg-white p-6 rounded-lg shadow-lg gap-16">
|
|
<h1 className="text-lg font-semibold">Encoach Detection Results</h1>
|
|
<div className="flex flex-row -md:flex-col -lg:gap-0 -xl:gap-10 gap-20 items-stretch -md:items-center">
|
|
<div className="flex -md:w-5/6 w-1/2 justify-center">
|
|
<div className="flex flex-col border rounded-xl">
|
|
<h1 className="border-b p-6 font-medium">Text Classification</h1>
|
|
<div className="flex flex-row gap-8 items-center p-6">
|
|
<RadialProgressBar
|
|
percentage={percentage}
|
|
text={predicted_class}
|
|
color={spanTextColor}
|
|
spanClassName={spanClassName}
|
|
/>
|
|
<div className="flex flex-col gap-1 text-sm">
|
|
<div className="flex flex-row items-center">
|
|
<span className="mr-2 text-ai-detection-result-ai-text font-semibold text-xl">
|
|
{`${Math.round(class_probabilities["ai"] * 100)}%`}
|
|
</span>
|
|
<span className="text-sm -md:text-xs text-ai-detection-text">Probability AI generated</span>
|
|
<a data-tooltip-id="probability-tooltip" data-tooltip-html={probabilityTooltipContent} className='ml-1 flex items-center justify-center'>
|
|
<Image src="/mat-icon-info.svg" width={24} height={24} alt="Probability Tooltip" />
|
|
</a>
|
|
</div>
|
|
<div className="flex flex-row items-center gap-1">
|
|
<div className={clsx(
|
|
"rounded-full w-3 h-3",
|
|
confidence_category == 'low' ?
|
|
"bg-ai-detection-confidence-low border border-ai-detection-confidence-border" : "bg-ai-detection-confidence-low-transparent"
|
|
)}></div>
|
|
<div className={clsx(
|
|
"rounded-full w-3 h-3",
|
|
confidence_category == 'medium' ?
|
|
"bg-ai-detection-confidence-medium border border-ai-detection-confidence-border" : "bg-ai-detection-confidence-medium-transparent"
|
|
)}></div>
|
|
<div className={clsx(
|
|
"rounded-full w-3 h-3 mr-2",
|
|
confidence_category == 'high' ?
|
|
"bg-ai-detection-confidence-high border border-ai-detection-confidence-border" : "bg-ai-detection-confidence-high-transparent"
|
|
)}></div>
|
|
<span className="text-sm -md:text-xs text-ai-detection-text">{keywords.join(' ')}</span>
|
|
<a data-tooltip-id="confidence-tooltip" data-tooltip-html={confidenceTooltipContent} className='ml-1 flex items-center justify-center'>
|
|
<Image src="/mat-icon-info.svg" width={24} height={24} alt="Probability Tooltip" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col border rounded-xl -md:w-5/6 w-2/6">
|
|
<h1 className="border-b p-6 font-medium">Probability Breakdown</h1>
|
|
<div className="flex items-center w-full h-full">
|
|
<SegmentedProgressBar segments={segments} className="w-full px-8 -md:py-8 text-ai-detection-text" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<div className="flex flex-row items-center">
|
|
<div dangerouslySetInnerHTML={{ __html: styledText }} className="mr-2"></div>
|
|
<div className={clsx(
|
|
"flex items-center justify-center p-2 rounded",
|
|
classPrediction[predicted_class]['color'],
|
|
classPrediction[predicted_class]['background']
|
|
)}>
|
|
<span className="text-sm">{classPrediction[predicted_class]['text']}</span>
|
|
</div>
|
|
</div>
|
|
{(hasHighlightedForAI && <div>
|
|
Sentences that are likely written by AI are <span className="font-semibold bg-ai-detection-highlight">highlighted</span>.
|
|
</div>)}
|
|
</div>
|
|
</div >
|
|
<div>
|
|
{sentences.map((item, index) => (
|
|
<span
|
|
key={index}
|
|
className={item.highlight_sentence_for_ai ? 'bg-ai-detection-highlight' : ''}
|
|
>
|
|
{item.sentence}{' '}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
export default AIDetection;
|