Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ Any use of third-party trademarks or logos are subject to those third-party's po

## Running

- `PERSPECTIVE_API_KEY=<perspective-api-key> TRANSFORMERS_CACHE=<path-to-transformers-cache> FLASK_APP=transformerviz/server.py flask run --host 0.0.0.0 --port 5000` (The `TRANSFORMERS_CACHE` environment variable is optional).
- `PERSPECTIVE_API_KEY=<perspective-api-key> GPT2_MODEL_VERSION=<gpt2-model-version> NUM_RETURN_SEQUENCES=<number-of-return-sequences> TRANSFORMERS_CACHE=<path-to-transformers-cache> FLASK_APP=transformerviz/server.py flask run --host 0.0.0.0 --port 5000` (The `TRANSFORMERS_CACHE` environment variable is optional).
- Point your browser to port 5000 of the server on which the app is running.

3 changes: 2 additions & 1 deletion frontend/MainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const Container: React.FunctionComponent<ContainerProps> = ({
loading={loading}
error={error}
onSelectTextId={selectText}
selectedTextIds={selectedTextIds} />
selectedTextIds={selectedTextIds}
/>
{getDetailedAnalysisComponent()}
</div>
</div>
Expand Down
40 changes: 32 additions & 8 deletions frontend/components/PerspectiveScoresBarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ReactDOM from "react-dom";
import * as d3 from "d3";
import { DirectionalHint } from 'office-ui-fabric-react/lib/Tooltip';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import AnalyzedText from "../models/AnalyzedText";


type PerspectiveScoresBarChartState = {
Expand All @@ -12,8 +13,8 @@ type PerspectiveScoresBarChartState = {
type PerspectiveScoresBarChartProps = {
id: number,
width: number,
scores: any,
defaultSelectedScore: string
defaultSelectedScore: string,
analyzedText: AnalyzedText
}

class PerspectiveScoresBarChart extends Component<PerspectiveScoresBarChartProps, PerspectiveScoresBarChartState> {
Expand Down Expand Up @@ -41,6 +42,12 @@ class PerspectiveScoresBarChart extends Component<PerspectiveScoresBarChartProps
this.createDistributionBarChart();
}

componentWillReceiveProps(nextProps) {
this.setState({
selectedScore: nextProps.defaultSelectedScore
});
}

setSelectedScore = (selectedScore: string) => {
this.setState({
selectedScore: selectedScore
Expand All @@ -51,6 +58,23 @@ class PerspectiveScoresBarChart extends Component<PerspectiveScoresBarChartProps
var _this = this;
var body = d3.select(this.node.current);

const getResultScoreObj: any = (item: any) => {
let resultScores = [
{label: "toxicity", value: item.toxicity, scoreName: "Toxicity"},
{label: "severeToxicity", value: item.severeToxicity, scoreName: "Severe Toxicity"},
{label: "identityAttack", value: item.identityAttack, scoreName: "Identity Attack"},
{label: "insult", value: item.insult, scoreName: "Insult"},
{label: "profanity", value: item.profanity, scoreName: "Profanity"},
{label: "threat", value: item.threat, scoreName: "Threat"},
{label: "sexuallyExplicit", value: item.sexuallyExplicit, scoreName: "Sexually Explicit"},
{label: "flirtation", value: item.flirtation, scoreName: "Flirtation"}
];

return resultScores;
}

var scores = getResultScoreObj(this.props.analyzedText);

var margin = { top: 5, right: 15, bottom: 50, left: 55 }
var h = 111 - margin.top - margin.bottom
var w = this.props.width;
Expand All @@ -69,10 +93,10 @@ class PerspectiveScoresBarChart extends Component<PerspectiveScoresBarChartProps
var g = svg.append("g")
.attr("transform", "translate(0," + margin.top + ")");

xScale.domain(this.props.scores.map(function(d) { return d.score; }));
xScale.domain(scores.map(function(d) { return d.label; }));
yScale.domain([0.0, 1.0]);

var selectedScoreObj = this.props.scores.filter(scoreObj => (scoreObj.score == this.state.selectedScore))[0];
var selectedScoreObj = scores.filter(scoreObj => (scoreObj.label == this.state.selectedScore))[0];
var selectedScoreMessage = `${selectedScoreObj.scoreName}: ${selectedScoreObj.value.toFixed(2)}`;

g.append("g")
Expand All @@ -86,16 +110,16 @@ class PerspectiveScoresBarChart extends Component<PerspectiveScoresBarChartProps
.text(selectedScoreMessage);

g.selectAll(".bar")
.data(this.props.scores)
.data(scores)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.score); })
.attr("x", function(d) { return xScale(d.label); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return h - yScale(d.value); })
.classed("highlighted-bar", function(d) { return d.score == _this.state.selectedScore })
.classed("highlighted-bar", function(d) { return d.label == _this.state.selectedScore })
.on("click", function(d) {
_this.setSelectedScore(d.score);
_this.setSelectedScore(d.label);
});
}

Expand Down
26 changes: 14 additions & 12 deletions frontend/components/TextGenerationControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,20 @@ class TextGenerationControl extends React.Component<
}

generateTextClickHandler = () => {
this.props.generateText({
prompt: this.state.textGenerationPrompt,
model: this.state.model,
do_sample: this.state.doSample,
early_stopping: this.state.earlyStopping,
min_length: this.state.minLength,
max_length: this.state.maxLength,
top_k: this.state.topK,
top_p: this.state.topP,
temperature: this.state.temperature,
num_beams: this.state.numBeams,
});
if (this.state.textGenerationPrompt.trim().length != 0) {
this.props.generateText({
prompt: this.state.textGenerationPrompt,
model: this.state.model,
do_sample: this.state.doSample,
early_stopping: this.state.earlyStopping,
min_length: this.state.minLength,
max_length: this.state.maxLength,
top_k: this.state.topK,
top_p: this.state.topP,
temperature: this.state.temperature,
num_beams: this.state.numBeams,
});
}
};

onTextPromptChange = (evt) => {
Expand Down
94 changes: 70 additions & 24 deletions frontend/components/TextGenerationResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,66 @@ type TextGenerationResultsProps = {
error?: any
}

class TextGenerationResults extends React.Component<TextGenerationResultsProps, null> {
type TextGenerationResultsState = {
analysisResults: AnalyzedText[],
highlightScoreLabel: string,
sortByLabel: string
}

class TextGenerationResults extends React.Component<TextGenerationResultsProps, TextGenerationResultsState> {

constructor(props) {
super(props);

this.state = {
analysisResults: props.analysisResults,
highlightScoreLabel: "toxicity",
sortByLabel: "none"
}
}

componentWillReceiveProps(nextProps) {
this.setState({
analysisResults: nextProps.analysisResults
});
}

sortGeneratedTextResults(scoreLabel) {
if (scoreLabel == "none") {
this.setState({
sortByLabel: scoreLabel,
analysisResults: this.props.analysisResults
});
} else {
const sortedAnalysisResults = this.state.analysisResults.map((result) => result).sort((resultA, resultB) => {
if (resultA[scoreLabel] < resultB[scoreLabel]) {
return -1;
} else if (resultA[scoreLabel] == resultB[scoreLabel]) {
return 0;
} else {
return 1;
}
});

this.setState({
sortByLabel: scoreLabel,
analysisResults: sortedAnalysisResults
});
}
}

setHighlightedScoreLabel(scoreLabel) {
if (scoreLabel != this.state.highlightScoreLabel) {
this.setState({
analysisResults: this.props.analysisResults,
highlightScoreLabel: scoreLabel,
sortByLabel: "none"
});
}
}

render() {
console.log(this.state);

if (this.props.error != null) {
return (
Expand All @@ -34,41 +87,26 @@ class TextGenerationResults extends React.Component<TextGenerationResultsProps,
);
}

const getResultScoreObj: any = (item: any) => {
let resultScores = [
{score: "TOXICITY", value: item.toxicity, scoreName: "Toxicity"},
{score: "SEVERE_TOXICITY", value: item.severeToxicity, scoreName: "Severe Toxicity"},
{score: "IDENTITY_ATTACK", value: item.identityAttack, scoreName: "Identity Attack"},
{score: "INSULT", value: item.insult, scoreName: "Insult"},
{score: "PROFANITY", value: item.profanity, scoreName: "Profanity"},
{score: "THREAT", value: item.threat, scoreName: "Threat"},
{score: "SEXUALLY_EXPLICIT", value: item.sexuallyExplicit, scoreName: "Sexually Explicit"},
{score: "FLIRTATION", value: item.flirtation, scoreName: "Flirtation"}
];

return resultScores;
}

const sortOptions: IComboBoxOption[] = [
{ key: 'none', text: 'None'},
{ key: 'toxicity', text: 'Toxicity' },
{ key: 'severe', text: 'Severe Toxicity' },
{ key: 'severeToxicity', text: 'Severe Toxicity' },
{ key: 'identity', text: 'Identity Attack' },
{ key: 'insult', text: 'Insult' },
{ key: 'profanity', text: 'Profanity' },
{ key: 'threat', text: 'Threat' },
{ key: 'sexually', text: 'Sexually Explicit' },
{ key: 'sexuallyExplicit', text: 'Sexually Explicit' },
{ key: 'flirtation', text: 'Flirtation' },
];

const scoreOptions: IComboBoxOption[] = [
{ key: 'all', text: 'All Scores' },
{ key: 'toxicity', text: 'Toxicity' },
{ key: 'severe', text: 'Severe Toxicity' },
{ key: 'identity', text: 'Identity Attack' },
{ key: 'severeToxicity', text: 'Severe Toxicity' },
{ key: 'identityAttack', text: 'Identity Attack' },
{ key: 'insult', text: 'Insult' },
{ key: 'profanity', text: 'Profanity' },
{ key: 'threat', text: 'Threat' },
{ key: 'sexually', text: 'Sexually Explicit' },
{ key: 'sexuallyExplicit', text: 'Sexually Explicit' },
{ key: 'flirtation', text: 'Flirtation' },
];

Expand All @@ -83,20 +121,28 @@ class TextGenerationResults extends React.Component<TextGenerationResultsProps,
label="Sort by"
options={sortOptions}
styles={{root: {maxWidth: 160}}}
selectedKey={this.state.sortByLabel}
onChange={(evt, option) => {
this.sortGeneratedTextResults(option.key);
}}
/>
<ComboBox
label="Show score"
options={scoreOptions}
styles={{root: {maxWidth: 160}}}
selectedKey={this.state.highlightScoreLabel}
onChange={(evt, option) => {
this.setHighlightedScoreLabel(option.key);
}}
/>
</Stack>
{this.props.analysisResults.map((item) =>
{this.state.analysisResults.map((item) =>
<div className="flex items-stretch mb-4" style={{minHeight: "110px", border: this.props.selectedTextIds.includes(item.id) ? "solid #167DF5 2px" : "none"}}>
<div className="flex items-center flex-none px-1" style={{backgroundColor: "#8894B1", color: "white", fontSize: "18px", lineHeight: "20px"}}>
{item.id}
</div>
<div className="flex-none" style={{width: `${chartWidth}px`, border: "solid #E7E7E7 1px"}}>
<PerspectiveScoresBarChart id={item.id} width={chartWidth} scores={getResultScoreObj(item)} defaultSelectedScore="TOXICITY" />
<PerspectiveScoresBarChart id={item.id} width={chartWidth} analyzedText={item} defaultSelectedScore={this.state.highlightScoreLabel} />
</div>
<button
className="flex items-center flex-auto text-left px-4"
Expand Down
Loading