satyamr196's picture
added React app files for testing, build commands added to the readme.md metadata
e7185a2
import { React, useState, useRef, useEffect, forwardRef } from "react";
import { set, useForm } from "react-hook-form";
import { useReactToPrint } from "react-to-print";
import styled from "styled-components";
import axios from "axios";
import Button from "../styled_components/Button";
import { ProgressBar } from "primereact/progressbar";
import "primereact/resources/themes/lara-light-cyan/theme.css";
import Nav from "../styled_components/Nav";
import { InputText } from "primereact/inputtext";
import { Chart } from "primereact/chart";
import WER_GenderChart from "../styled_components/WER_GenderChart";
import { useContext } from "react";
import { DataContext } from "../DataContext";
import BoxPlot from "../styled_components/Box_plot";
import Speedometer from "../styled_components/Speedometer";
import { Toast } from "primereact/toast";
import { ScrollTop } from "primereact/scrolltop";
import ScoreCard from "../styled_components/Scorecard";
import { media } from "../Utils/helper";
import StopAudit from "../styled_components/StopAudit";
export function Request({ showSucess, showError, showInfo, baseUrl }) {
let [result, setResult] = useState(false);
let [isRunning, setIsRunning] = useState(false);
let [category_FS, setCategory_FS] = useState([]);
let [auditProgress, setAuditProgress] = useState(0);
const { sharedData, setSharedData } = useContext(DataContext);
let [fetchProgress, setFetchProgress] = useState(false);
const contentRef = useRef(null);
const reactToPrintFn = useReactToPrint({ contentRef });
const [isUpdated, setIsUpdated] = useState(false);
const getAuditProgress = async () => {
try {
const response = await axios.get(`${baseUrl}/status`);
setAuditProgress(Math.round(response.data["%_completed"] * 100) / 100);
} catch (error) {
console.error("Error fetching audit progress:", error);
}
};
const sendPostRequest = async (postData) => {
try {
const response = await axios.post(`${baseUrl}/insert`, postData);
if (response.data.exists) {
console.log("Already in DB");
showInfo();
} else {
console.log("Success:", response.data);
showSucess();
}
} catch (error) {
console.error("Error:", error);
showError();
}
};
const publishResult = () => {
const postData = {
Model: result["ASR_model"],
WER: Math.round(result["Avg_wer"] * 100) / 100,
RTFX: Math.round(result["Avg_rtfx"] * 100) / 100,
FAAS: Math.round(result["FAAS"] * 100) / 100,
FS: Math.round(result["Overall Fairness Score"] * 100) / 100,
FS_G:
Math.round(result["Adjusted Category Fairness Score"]["gender"] * 100) /
100,
FS_L:
Math.round(
result["Adjusted Category Fairness Score"]["first_language"] * 100
) / 100,
FS_SEG:
Math.round(
result["Adjusted Category Fairness Score"]["socioeconomic_bkgd"] * 100
) / 100,
FS_E:
Math.round(
result["Adjusted Category Fairness Score"]["ethnicity"] * 100
) / 100,
};
sendPostRequest(postData);
};
const {
register,
handleSubmit,
formState: { errors },
clearErrors,
trigger,
setValue,
} = useForm();
const onSubmit = async (data) => {
setIsRunning(true);
setFetchProgress(true);
const queryString = new URLSearchParams(data).toString();
try {
const res = await axios.get(`${baseUrl}/api?${queryString}`, {
headers,
timeout: 10800000,
});
setResult(res.data);
setIsRunning(false);
setCategory_FS([
{
category: "Gender",
FS: result["Adjusted Category Fairness Score"]?.["gender"],
},
{
category: "Language",
FS: result["Adjusted Category Fairness Score"]?.["first_language"],
},
{
category: "Ethnicity",
FS: result["Adjusted Category Fairness Score"]?.["ethnicity"],
},
{
category: "Socioeconomic BG",
FS: result["Adjusted Category Fairness Score"]?.[
"socioeconomic_bkgd"
],
},
]);
setSharedData({
...sharedData,
Request: res.data,
Category_FS: [
{
category: "Gender",
FS: result["Adjusted Category Fairness Score"]?.["gender"],
},
{
category: "Language",
FS: result["Adjusted Category Fairness Score"]?.["first_language"],
},
{
category: "Ethnicity",
FS: result["Adjusted Category Fairness Score"]?.["ethnicity"],
},
{
category: "Socioeconomic BG",
FS: result["Adjusted Category Fairness Score"]?.[
"socioeconomic_bkgd"
],
},
],
});
} catch (error) {
console.error("Retrying due to error:", error.message);
}
};
useEffect(() => {
let interval;
if (fetchProgress) {
interval = setInterval(() => {
getAuditProgress();
}, 3000);
}
// Cleanup function
return () => {
if (interval) clearInterval(interval);
};
}, [isRunning]); // runs whenever isRunning changes
useEffect(() => {
if (sharedData && sharedData.Request) {
setResult(sharedData.Request);
}
if (sharedData && sharedData.Category_FS) {
setCategory_FS(sharedData.Category_FS);
}
if (result && result["Adjusted Category Fairness Score"]) {
setCategory_FS([
{
category: "Gender",
FS: result["Adjusted Category Fairness Score"]["gender"],
},
{
category: "Language",
FS: result["Adjusted Category Fairness Score"]["first_language"],
},
{
category: "Ethnicity",
FS: result["Adjusted Category Fairness Score"]["ethnicity"],
},
{
category: "Socioeconomic BG",
FS: result["Adjusted Category Fairness Score"]["socioeconomic_bkgd"],
},
]);
}
}, [result]);
const headers = {
"ngrok-skip-browser-warning": "10008",
};
return (<>
<Main>
<PrintContainer ref={contentRef}>
<h2>Check ASR Model Fairness Score</h2>
<InfoPanel>
<h4>How the Fairness Audit Works:</h4>
<ol>
<li>
Enter a Hugging Face ASR model ID (e.g.,{" "}
<ModelExample>facebook/wav2vec2-base-960h</ModelExample>)
</li>
<li>
Our system will run inference using a 10% stratified sample from
Meta's FairSpeech dataset
</li>
<li>
Results will show performance differences across demographic
groups including gender, language, ethnicity, and socioeconomic
background
</li>
</ol>
<Tip>
<i className="pi pi-info-circle"></i> The audit may take up to 60
minutes to complete depending on the model size and hardware.
</Tip>
<Tip>
<i className="pi pi-info-circle"></i> If the model has been
evaluated before, results appear instantly; otherwise, please wait
for the audit to finish.
</Tip>
<Tip>
<i className="pi pi-info-circle"></i> Please ensure that the ASR model you are requesting for audit is available for use within the Hugging Face Transformers library.
</Tip>
{/* <Tip>
<i className="pi pi-info-circle"></i> If audit for a model is in progress then please wait for it to finish,
then run fairness audit for your model.
</Tip> */}
</InfoPanel>
<form onSubmit={handleSubmit(onSubmit)}>
<FormGroup>
<label htmlFor="username">Hugging Face Model ID:</label>
<ModelInputWrapper>
<InputText
placeholder="Example: facebook/wav2vec2-base-960h"
variant="filled"
id="username"
{...register("ASR_model", {
required: "Please enter a valid model path",
maxLength: {
value: 96,
message: "Model ID must be at most 96 characters long",
},
pattern: {
value: /^[a-zA-Z0-9/_\-.]+$/,
message: "Model ID can only contain letters, digits, '/ ', '_', and '-'"
},
validate: {
noDoubleDashDot: v =>
!/--|\.\./.test(v) || "Model ID must not contain '--' or '..'",
noEdgeDashDot: v =>
!/^[-.]|[-.]$/.test(v) || "Model ID must not start or end with '-' or '.'"
}
})}
/>
</ModelInputWrapper>
{errors.ASR_model && (
<ErrorMessage>{errors.ASR_model.message}</ErrorMessage>
)}
<ExampleModels>
<span>Try these examples: </span>
<ExampleButton
type="button"
onClick={() =>
setValue("ASR_model", "facebook/wav2vec2-base-960h", {
shouldValidate: true,
shouldDirty: true,
})
}
>
wav2vec2
</ExampleButton>
<ExampleButton
type="button"
onClick={() =>
setValue("ASR_model", "openai/whisper-tiny", {
shouldValidate: true,
shouldDirty: true,
})
}
>
whisper-tiny
</ExampleButton>
<ExampleButton
type="button"
onClick={() =>
setValue("ASR_model", "openai/whisper-small", {
shouldValidate: true,
shouldDirty: true,
})
}
>
whisper-small
</ExampleButton>
</ExampleModels>
</FormGroup>
<BtnGroup>
<Button
type="submit"
shadow="blue"
bg="#3b82f6"
color="white"
disabled={isRunning}
>
Run Fairness Audit
</Button>
<StopAudit baseUrl={baseUrl} />
</BtnGroup>
</form>
{result.message && (
<h3 style={{ marginBottom: 0 }}>
<i>
<u>{result.message}</u>
</i>
<br />
{result.status && (
<><b>
Progress :{" "}
{Math.round(result.status["%_completed"] * 100) / 100} % </b> <I>( * at the time of request )</I>
</>
)}
</h3>
)}
{isRunning ? (
<>
<br />
<ProgressBar
color="#3b82f6"
mode="indeterminate"
style={{ height: "15px", borderRadius: "1000px" }}
/>
</>
) : (
auditProgress > 0 &&
auditProgress < 100 &&
!result.ASR_model && (
<>
<ProgressBar
color="#3b82f6"
value={auditProgress}
style={{
height: "15px",
fontSize: "13px",
borderRadius: "9000px",
marginBlock: "1.2rem",
}}
/>
</>
)
)}
{result.ASR_model && (
<>
{" "}
<br></br>
<GraphContainer>
<PageBreakContainer>
<Head>Summary</Head>
<br></br>
<Summary_wrap>
<div>
<ScoreCard
label="Fairness-Adjusted ASR Score (FAAS) : "
width={"100%"}
score={result["FAAS"]}
fontWeight={700}
></ScoreCard>
</div>
<Summary_Chart>
<Speedometer
value={result["Overall Fairness Score"]}
></Speedometer>
<WER_GenderChart
graphData={category_FS}
title="Fairness Score (By Category)"
labelY="Fairness Score"
xAxis="category"
yAxis="FS"
angle={-45}
bMargin={80}
height="400px"
colorStroke="rgb(2, 167, 5)"
colorFill="rgba(24, 219, 27, 0.447)"
/>
</Summary_Chart>
</Summary_wrap>
<br></br>
<br></br>
<hr></hr>
</PageBreakContainer>
<PageBreakContainer>
<Head>Gender</Head>
<br></br>
<Box_chartV>
<ScoreCard
label="Fairness Score : "
width={"100%"}
score={result["Adjusted Category Fairness Score"]["gender"]}
></ScoreCard>
<BoxPlot werData={result.wer_Gender} title="Gender"></BoxPlot>
</Box_chartV>
</PageBreakContainer>
<PageBreakContainer>
<Head>Socioeconomic Background</Head>
<br></br>
<Box_chartV>
<ScoreCard
label="Fairness Score : "
width={"100%"}
score={
result["Adjusted Category Fairness Score"][
"socioeconomic_bkgd"
]
}
></ScoreCard>
<BoxPlot
werData={result.wer_SEG}
title="Socioeconomic Background"
></BoxPlot>
</Box_chartV>
</PageBreakContainer>
<PageBreakContainer>
<Head>Language</Head>
<br></br>
<Box_chartV>
<ScoreCard
label="Fairness Score : "
width={"100%"}
score={
result["Adjusted Category Fairness Score"][
"first_language"
]
}
></ScoreCard>
<BoxPlot
werData={result.wer_Language}
title="Language"
mb={80}
></BoxPlot>
</Box_chartV>
</PageBreakContainer>
<PageBreakContainer>
<Head>Ethnicity</Head>
<br></br>
<Box_chartV>
<ScoreCard
label="Fairness Score : "
width={"100%"}
score={
result["Adjusted Category Fairness Score"]["ethnicity"]
}
></ScoreCard>
<BoxPlot
werData={result.wer_Ethnicity}
title="Ethnicity"
mb={165}
></BoxPlot>
</Box_chartV>
</PageBreakContainer>
</GraphContainer>
<BtnGroup>
<Button
onClick={publishResult}
shadow="blue"
bg="#3b82f6"
color="white"
>
Publish to Leaderboard
</Button>
<Button
shadow="blue"
bg="#3b82f6"
color="white"
onClick={() => reactToPrintFn()}
>
Export PDF
</Button>
</BtnGroup>
<ScrollTop />
</>
)}
</PrintContainer>
</Main>
</>
);
}
const Main = styled.div`
/* .p-progressbar-value {
background-color: #3b82f6;
} */
`;
const I = styled.i`
font-size: 0.8rem;
color: #979797;
font-weight: 100;
`
const PrintContainer = styled.div`
@media print {
margin: 1.5rem; /* Add 1-inch margin on all sides */
margin-top: 1rem !important;
/* padding: 0.5in; Add padding inside the content */
box-sizing: border-box;
}
`;
// New styled components for the form
const InfoPanel = styled.div`
background-color: #f0f7ff;
border-left: 4px solid #3b82f6;
padding: 1rem 1.5rem;
margin-bottom: 1.5rem;
border-radius: 0.25rem;
h4 {
margin-top: 0;
margin-bottom: 0.75rem;
font-size: 1.1rem;
font-weight: 600;
}
ol {
margin-left: 1.5rem;
margin-bottom: 0.75rem;
@media ${media.mobile} {
margin-left: 0.5rem;
margin-bottom: 0.5rem;
padding-left: 0.9rem;
}
}
li {
margin-bottom: 0.5rem;
}
`;
const Tip = styled.p`
font-size: 1rem;
margin-top: 0.5rem;
color: #3b82f6;
font-style: italic;
i {
margin-right: 0.5rem;
}
@media ${media.tablet} {
font-size: 0.9rem;
margin-top: 0.3rem;
}
@media ${media.mobile} {
font-size: 0.8rem;
margin-top: 0.3rem;
}
`;
const ModelExample = styled.code`
background: rgba(0, 0, 0, 0.05);
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-family: monospace;
font-size: 0.9em;
`;
const FormGroup = styled.div`
margin-bottom: 1rem;
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
align-items: center;
justify-content: center;
}
`;
const ModelInputWrapper = styled.div`
display: flex;
align-items: center;
margin-bottom: 0.5rem;
input {
margin-bottom: 0.5rem !important;
font-family: "Poppins", monospace;
width: 60%;
@media ${media.tablet} {
width: 80%;
}
@media ${media.mobile} {
width: 95%;
}
}
`;
const ModelPrefix = styled.div`
background-color: #f3f4f6;
padding: 0.6rem 0.75rem;
border: 1px solid #d1d5db;
border-right: none;
border-radius: 0.25rem 0 0 0.25rem;
color: #6b7280;
font-family: monospace;
`;
const ErrorMessage = styled.div`
color: #ef4444;
font-size: 0.875rem;
margin-top: 0.25rem;
margin-bottom: 0.6rem;
`;
const ExampleModels = styled.div`
margin-bottom: 1rem;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
span {
color: #6b7280;
font-size: 0.875rem;
}
`;
const ExampleButton = styled.button`
background: none;
border: 1px solid #d1d5db;
border-radius: 0.25rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
cursor: pointer;
color: #374151;
&:hover {
background-color: #f3f4f6;
}
`;
// Add this styled component for page break handling
const PageBreakContainer = styled.div`
page-break-inside: avoid; /* Prevent breaking inside this container */
break-inside: avoid; /* For modern browsers */
margin-bottom: 2rem; /* Add spacing between sections */
`;
// Keep all original styled components
export const Head = styled.h6`
font-size: 2rem;
color: #3b82f6;
margin-block: 0;
text-decoration: 4px solid underline;
`;
const GraphContainer = styled.div`
display: flex;
flex-direction: column;
gap: 2.5rem;
`;
const ComboDivG = styled.div`
flex-grow: 1;
`;
const ComboDivS = styled.div`
flex-grow: 1.5;
`;
const Combo_Chart = styled.div`
display: flex;
gap: 2rem;
justify-content: space-between;
`;
const Summary_wrap = styled.div`
display: flex;
flex-direction: column;
`;
const Summary_Chart = styled.div`
display: flex;
gap: 2rem;
min-height: 60vh;
justify-content: space-between;
align-content: center;
align-items: center;
flex-direction: column;
@media (min-width: 1028px) {
flex-direction: row;
}
`;
const Box_chartV = styled.div`
display: flex;
flex-direction: column;
min-height: 60vh;
justify-content: space-between;
align-content: center;
align-items: center;
`;
const BtnGroup = styled.div`
display: flex;
flex-wrap: wrap;
gap: 2rem;
/* margin-top: 1rem; */
`;