Spaces:
Running
Running
| 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; */ | |
| `; | |