Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<title>Responsible Prompting Demo</title> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" href="https://unpkg.com/carbon-components/css/carbon-components.min.css"> | |
<script type="text/javascript" src="static/demo/js/d3.v7.min.js"></script> | |
<script type="text/javascript" src="static/demo/js/jquery-3.7.1.min.js"></script> | |
<!-- <script type="text/javascript" src="js/d3.v7.min.js"></script> | |
<script type="text/javascript" src="js/jquery-3.7.1.min.js"></script> --> | |
<style type="text/css"> | |
div.tooltip { | |
position: absolute; | |
text-align: left; | |
padding: 0.5em; | |
width: 20em; | |
min-height: 5em; | |
background: #fff; | |
color: #000; | |
border: 1px solid #000; | |
border-radius: 5px; | |
pointer-events: none; | |
font-size: inherit; | |
font-family: inherit; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="bx--grid"> | |
<div class="bx--row"> | |
<div class="bx--col-lg-10 bx--offset-lg-1"> | |
<h1 class="bx--type-expressive-heading-03" style="margin-top: 1em;">Responsible Prompting</h1> | |
<p class="bx--type-body-long-01"><br>Please provide a prompt that would be sent to an LLM. Recommendations are performed in prompting-time, before content generation. The recommendations consider a curated dataset of values and prompts sentences. The recommendations are based on the similarity between the input sentences and our prompt sentences dataset.<br><br></p> | |
<div class="bx--tabs"> | |
<ul class="bx--tabs__nav"> | |
<li class="bx--tabs__nav-item bx--tabs__nav-item--selected" id="tab-1" data-target="#tab-content-1"> | |
<a class="bx--tabs__nav-link" href="#" onclick="$('#tab-content-1').toggle();$('#tab-1').toggleClass('bx--tabs__nav-item--selected');$('#tab-content-2').toggle();$('#tab-2').toggleClass('bx--tabs__nav-item--selected');">Prompt</a> | |
</li> | |
<li class="bx--tabs__nav-item" data-target="#tab-content-2" id="tab-2"> | |
<a class="bx--tabs__nav-link" href="#" onclick="$('#tab-content-1').toggle();$('#tab-1').toggleClass('bx--tabs__nav-item--selected');$('#tab-content-2').toggle();$('#tab-2').toggleClass('bx--tabs__nav-item--selected');renderGraph($('#prompt').data('recommendations'))">Graph</a> | |
</li> | |
</ul> | |
</div> | |
<form class="bx--form" id="demo"> | |
<div class="bx--tab-content"> | |
<div id="tab-content-1" class="bx--tab-content__section"> | |
<div class="bx--form-item"> | |
<textarea id="prompt" name="prompt" class="bx--text-area" rows="15" placeholder="Enter your prompt">Act as a professional designer with 20 years of experience creating and testing UX interfaces and landing sites for a variety of IT applications. We are in need of more people and an increased budget to be able to keep up with clients' needs. What kind of evidence should I gather to support my demands to gain more resources?</textarea> | |
<div id="recommendation" style="min-height: 3em;" class="bx--form__helper-text"></div> | |
</div> | |
</div> | |
<div id="tab-content-2" class="bx--tab-content__section bx--tab-content--hidden" style="display: none;"> | |
<div class="bx--form-item" id="graph" style="background: #efefef"></div> | |
</div> | |
</div> | |
<div class="bx--form-item" style="margin-bottom: 2em;"> | |
<button id="generate" type="submit" class="bx--btn bx--btn--primary">Generate</button> | |
</div> | |
<div class="bx--form-item"> | |
<label for="outcome" id="outcome-label" class="bx--label">Generated text</label> | |
<textarea id="outcome" name="outcome" class="bx--text-area" rows="12" placeholder=""></textarea> | |
</div> | |
</form> | |
</div> | |
</div> | |
</div> | |
<script lang="javascript"> | |
const width = 600; | |
const height = 200; | |
const marginTop = 30; | |
const marginRight = 30; | |
const marginBottom = 30; | |
const marginLeft = 30; | |
const nodeRadius = 3; | |
const svg = d3.select("#graph").append("svg") | |
.attr("viewBox", [0, 0, width, height]) | |
.attr("style", "max-width: 100%; height: auto; font: 8px sans-serif;"); | |
var tooltip = d3.select("body").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
const renderGraph = (recommendations) => { | |
if( !recommendations ){ | |
return ; | |
} | |
svg.selectAll("*").remove(); // Clearing previous plots | |
var graphData = {nodes: [], edges: []} | |
var i = j = 0; | |
if( recommendations['input'].length > 0 ){ | |
graphData.nodes.push({ | |
id: 0, | |
x: Number(recommendations['input'][0].x), | |
y: Number(recommendations['input'][0].y), | |
text: recommendations['input'][0].sentence, | |
label: 'S' + (i+1), | |
type: 'input' | |
}) | |
for( i = 1; i < recommendations['input'].length; i++ ){ | |
graphData.nodes.push({ | |
id: i, | |
x: Number(recommendations['input'][i].x), | |
y: Number(recommendations['input'][i].y), | |
text: recommendations['input'][i].sentence, | |
label: 'S' + (i+1), | |
type: 'input' | |
}); | |
graphData.edges.push({ source: (i-1), target: i, type: 'input' }); | |
} | |
} | |
// Adding nodes & edges for inclusion recommendations | |
if( recommendations['add'].length > 0 ){ | |
for( j = 0; j < recommendations['add'].length; j++ ){ | |
graphData.nodes.push({ | |
id: i + j + 1, | |
x: Number(recommendations['add'][j].x), | |
y: Number(recommendations['add'][j].y), | |
text: recommendations['add'][j].prompt, | |
label: recommendations['add'][j].value, | |
type: 'add' | |
}); | |
graphData.edges.push({ source: (i-1), target: (i+j+1), type: 'add' }); | |
} | |
} | |
// Adding nodes & edges for removal recommendations | |
if( recommendations['remove'].length > 0 ){ | |
// Showing only the first removal recommendation | |
graphData.nodes.push({ | |
id: i + j, | |
x: Number(recommendations['remove'][0].x), | |
y: Number(recommendations['remove'][0].y), | |
text: recommendations['remove'][0].closest_harmful_sentence, | |
label: recommendations['remove'][0].value, | |
type: 'remove' | |
}); | |
} | |
// Convert edge references to actual node objects | |
graphData.edges = graphData.edges.map(edge => ({ | |
source: graphData.nodes.find(n => n.id === edge.source), | |
target: graphData.nodes.find(n => n.id === edge.target) | |
})); | |
const { nodes, edges } = graphData; | |
// Prepare the ranges of values for the axes | |
const xDomain = d3.extent(nodes, d => d.x); | |
const yDomain = d3.extent(nodes, d => d.y); | |
const xPadding = 2 | |
const yPadding = 2 | |
// Prepare the scales for positional encoding. | |
const x = d3.scaleLinear() | |
.domain([xDomain[0]-xPadding,xDomain[1]+xPadding]).nice() | |
.range([marginLeft, width - marginRight]); | |
const y = d3.scaleLinear() | |
.domain([yDomain[0]-yPadding,yDomain[1]+yPadding]).nice() | |
.range([height - marginBottom, marginTop]); | |
// Create the axes. | |
svg.append("g") | |
.attr("transform", `translate(0,${height - marginBottom})`) | |
.call(d3.axisBottom(x).ticks(width / 80)) | |
.call(g => g.select(".domain").remove()) | |
.call(g => g.append("text") | |
.attr("x", width) | |
.attr("y", marginBottom - 4) | |
.attr("fill", "currentColor") | |
.attr("text-anchor", "end") | |
.text("Semantic dimension 1")); | |
svg.append("g") | |
.attr("transform", `translate(${marginLeft},0)`) | |
.call(d3.axisLeft(y)) | |
.call(g => g.select(".domain").remove()) | |
.call(g => g.append("text") | |
.attr("x", -marginLeft) | |
.attr("y", 10) | |
.attr("fill", "currentColor") | |
.attr("text-anchor", "start") | |
.text("Semantic dimension 2")); | |
// Create the grid. | |
svg.append("g") | |
.attr("stroke", "#cccccc") | |
.attr("stroke-opacity", 0.5) | |
.call(g => g.append("g") | |
.selectAll("line") | |
.data(x.ticks()) | |
.join("line") | |
.attr("x1", d => 0.5 + x(d)) | |
.attr("x2", d => 0.5 + x(d)) | |
.attr("y1", marginTop) | |
.attr("y2", height - marginBottom)) | |
.call(g => g.append("g") | |
.selectAll("line") | |
.data(y.ticks()) | |
.join("line") | |
.attr("y1", d => 0.5 + y(d)) | |
.attr("y2", d => 0.5 + y(d)) | |
.attr("x1", marginLeft) | |
.attr("x2", width - marginRight)); | |
// Add a layer of dots. | |
svg.append("g") | |
.attr("stroke-width", 2.5) | |
.attr("stroke-opacity", 0.5) | |
.attr("fill", "none") | |
.selectAll("circle") | |
.data(nodes) | |
.join("circle") | |
.attr("stroke", d => d.type == "add" ? "green" : d.type == "input"? "#666" : "red" ) | |
.attr("cx", d => x(d.x)) | |
.attr("cy", d => y(d.y)) | |
.attr("r", nodeRadius); | |
// Add a layer of labels. | |
svg.append("g") | |
.attr("font-family", "sans-serif") | |
.attr("text-opacity", 0.5) | |
.attr("font-size", 8) | |
.selectAll("text") | |
.data(nodes) | |
.join("text") | |
.attr("dy", "0.35em") | |
.attr("x", d => x(d.x)+5) | |
.attr("y", d => y(d.y)) | |
.text(d => d.label) | |
.on('mousemove', function (d, i) { | |
d3.select(this).transition() | |
.duration("50") | |
.attr("text-opacity", 1.0) | |
.attr("stroke", "white") | |
.attr("stroke-width", 3) | |
.style("paint-order", "stroke fill") | |
.attr("fill", d => d.type == "add" ? "green" : d.type == "input"? "black" : "red" ) | |
tooltip.transition() | |
.duration(50) | |
.style("opacity", 1); | |
tooltip.html( "<strong>" + i.label + ":</strong><br>" + i.text ) | |
.style("left", (event.pageX + 10) + "px") | |
.style("top", (event.pageY + 10) + "px"); | |
; | |
}) | |
.on('mouseout', function (d, i) { | |
d3.select(this).transition() | |
.duration("50") | |
.attr("text-opacity", 0.5) | |
.attr("stroke-width", 0) | |
.style("paint-order", "stroke fill") | |
.attr("fill", "black") | |
tooltip.transition() | |
.duration(50) | |
.style("opacity", 0); | |
}); | |
// Adding edges | |
svg.append("g") | |
.selectAll("line") | |
.data(edges) | |
.join("line") | |
.attr("stroke", "#666") | |
.attr("stroke-opacity", 0.5) | |
.attr("x1", d => x(d.source.x)+(d.source.x<d.target.x?1.3*nodeRadius:nodeRadius*-1.3)) | |
.attr("y1", d => y(d.source.y)) | |
.attr("x2", d => x(d.target.x)+(d.source.x>d.target.x?1.3*nodeRadius:nodeRadius*-1.3)) | |
.attr("y2", d => y(d.target.y)) | |
.style("stroke-dasharray", d => d.target.type == "add" ? "3,3" : ""); | |
}; | |
// ------------------------------------------------ | |
// Init state | |
if( $( "#prompt" ).val() == "" ){ | |
$( "#generate" ).attr( "disabled", true ) ; | |
} | |
var last_request = Date.now() - 60 * 60 * 1000 ; | |
var last_prompt = $( "#prompt" ).val().trim() ; | |
// Add recommendations to the prompt | |
function add_recommendation( p ){ | |
preview_add_recommendation( p, "hide" ) | |
$( "#prompt" ).val( $( "#prompt" ).val() + " " + p ) ; | |
$( "#recommendation" ).html( "" ) ; | |
$( "#prompt" ).trigger( "keyup" ) ; // Looking for recommendations after accepting a recommendation | |
} | |
// Preview for add recommendation | |
function preview_add_recommendation( p, flag ){ | |
if( flag == "show" ){ | |
$( "#prompt" ).val( $( "#prompt" ).val() + " " + p ) ; | |
} | |
else{ | |
$( "#prompt" ).val( $( "#prompt" ).val().replace( " " + p, "" ) ) ; | |
} | |
} | |
// Remove adversarial sentences from prompt | |
function remove_recommendation( p ){ | |
$( "#prompt" ).val( $( "#prompt" ).val().replace( p, "" ) ) ; | |
$( "#prompt" ).val( $( "#prompt" ).val().replace( " ", " " ) ) ; | |
$( "#recommendation" ).html( "" ) ; | |
$( "#prompt" ).trigger( "keyup" ) ; // Looking for recommendations after accepting a recommendation | |
} | |
// Preview for add recommendation | |
function preview_remove_recommendation( p, flag ){ | |
if( flag == "show" ){ | |
$( "#prompt" ).data( "previous_prompt", $( "#prompt" ).val() ) ; | |
$( "#prompt" ).val( $( "#prompt" ).val().replace( p, "" ) ) ; | |
} | |
else{ | |
$( "#prompt" ).val( $( "#prompt" ).data( "previous_prompt" ) ) ; | |
} | |
} | |
// Listening to changes performed on the prompt input field | |
$( "#prompt" ).on( "keyup", function( e ){ | |
// Updating the generate button state based on prompt length | |
if( $( "#prompt" ).val().length > 0 ){ | |
$( "#generate" ).removeAttr( "disabled" ) ; | |
} | |
else{ | |
$( "#generate" ).attr( "disabled", true ) ; | |
} | |
// Minimum timeout between the requests | |
if( Date.now() - last_request > 500 && last_prompt != $( "#prompt" ).val().trim() ){ | |
last_request = Date.now() ; | |
last_prompt = $( "#prompt" ).val().trim() ; | |
// Getting the last typed char | |
var last_char = $( "#prompt" ).val().trim().slice( -1 ) ; | |
// Triggering the API request when ending of a sentence is found, e.g., .?! | |
if( last_char == "." || last_char == "?" || last_char == "!" ){ | |
$( "#recommendation" ).html( 'Requesting recommendations: <div class="bx--tag bx--tag--gray bx--tag--deletable">...</div>' ) ; | |
var api_url = "/recommend?prompt=" | |
// var api_url = "/recommend_local?prompt=" | |
var p = $( "#prompt" ).val() ; | |
// API request | |
$.getJSON( api_url + encodeURI( p ), function( data ) { | |
$( "#recommendation" ).html( "Recommendations: " ) ; | |
// Looking first for removal recommendations | |
// if( data["remove"].length > 0 && data["remove"][0].similarity > 0.5 ){ | |
if( data["remove"].length > 0 ){ | |
for( var i = 0; i < data["remove"].length ; i++ ){ | |
// $( "#prompt" ).html( $( "#prompt" ).html().replace( data["remove"][i].sentence, '<span style="text-decoration: underline red">' + data["remove"][i].sentence + '</span>' ) ) ; | |
var sentence = data["remove"][i].sentence.replaceAll( "'", "\\'" ) ; | |
var sentence_entity = data["remove"][i].sentence.replaceAll( "'", "'" ) ; | |
var sentence_value = data["remove"][i].value.replaceAll( "'", "\\'" ) ; | |
$( "#recommendation" ).append( '<div class="bx--tag bx--tag--red bx--tag--deletable" style="cursor: pointer;" onmouseover="preview_remove_recommendation(\''+ sentence + '\', \'show\')" onmouseout="preview_remove_recommendation(\''+ sentence + '\', \'hide\')" onclick="remove_recommendation(\''+ sentence + '\')">x ' + sentence_value + '</div>' ) ; | |
break ; // Showing only once removal recommendation at a time | |
} | |
} | |
// else if( data["add"].length > 0 ){ // After the removal recommendations are dealt with, then we show recommendations for inclusion | |
if( data["add"].length > 0 ){ // Think Demo UX | |
for( var i = 0; i < data["add"].length; i++ ){ | |
if( $( "#prompt" ).val().indexOf( data["add"][i].prompt ) == -1 ){ | |
// Adding only recommendations that are not present in the prompt | |
var sentence = data["add"][i].prompt.replaceAll( "'", "\\'" ) ; | |
var sentence_entity = data["add"][i].prompt.replaceAll( "'", "'" ) ; | |
var sentence_value = data["add"][i].value.replaceAll( "'", "\\'" ) ; | |
$( "#recommendation" ).append( '<div class="bx--tag bx--tag--green" style="cursor: pointer;" onmouseover="preview_add_recommendation(\''+ sentence + '\', \'show\')" onmouseout="preview_add_recommendation(\''+ sentence + '\', \'hide\')" onclick="add_recommendation(\''+ sentence + '\')">+ ' + sentence_value + '</div>' ) ; | |
} | |
} | |
} | |
// User status message about recommendations found | |
if( data["add"].length == 0 && data["remove"].length == 0 ){ | |
$( "#recommendation" ).html( "No recommendations found." ) ; | |
} | |
$("#prompt").data( "recommendations", data ); | |
// renderGraph(data); | |
}); | |
} | |
} | |
}); | |
// Generation request | |
$( "#demo" ).on( "submit", function(e){ // Hugging Face | |
$( "#generate" ).toggleClass( "bx--btn--disabled" ) ; | |
( function loading_animation(){ | |
if( $( "#outcome" ).attr( "placeholder" ) == "" ){ | |
$( "#outcome" ).attr( "placeholder", "Requesting content." ) ; | |
} | |
else if( $( "#outcome" ).attr( "placeholder" ).length < 21 ){ | |
$( "#outcome" ).attr( "placeholder", $( "#outcome" ).attr( "placeholder") + "." ) ; | |
} | |
else{ | |
$( "#outcome" ).attr( "placeholder", "Requesting content." ) ; | |
} | |
setTimeout( loading_animation, 500 ); | |
} )() | |
$.ajax({ | |
url: encodeURI("/demo_inference?prompt=" + $("#prompt").val()), | |
dataType: 'json', | |
success: function(data){ | |
console.log("Inference response") | |
console.log(data) | |
// Resetting the status of the button | |
$( "#generate" ).toggleClass( "bx--btn--disabled" ) ; | |
// Clearing the previous timeout | |
if( $( "#demo" ).data( "timeoutId" ) != "" ){ | |
clearTimeout( $( "#demo" ).data( "timeoutId" ) ); | |
$( "#demo" ).data( "timeoutId", "" ) ; | |
} | |
out = data.content.split(""); | |
model_id = data.model_id; | |
temperature = data.temperature | |
max_new_tokens = data.max_new_tokens | |
$( "#outcome" ).append( "\n\n+ ------------------------------------\n| Model: " + model_id + "\n| Temperature: " + temperature + "\n| Max new tokens: " + max_new_tokens + "\n+ ------------------------------------\n\n" ) ; | |
// Animating the generated output | |
( function typing_animation(){ | |
$( "#outcome" ).append( out.shift() ) ; | |
$( "#outcome" ).scrollTop( $( "#outcome" )[0].scrollHeight ) ; | |
if( out.length > 0 ) { | |
const timeoutId = setTimeout( typing_animation, 5 ); | |
$( "#demo" ).data( "timeoutId", timeoutId ) ; | |
} | |
} )() | |
}, | |
error: function(data) { | |
out = data.responseJSON.error.message | |
$( "#outcome" ).val(out); | |
} | |
}) | |
// Returning false so the form keeps user in the same page | |
return false; | |
}); | |
</script> | |
<!-- <script src="https://unpkg.com/carbon-components/scripts/carbon-components.min.js"></script> --> | |
</body> | |
</html> | |