/**
* CA318 Practical Project
* Calem McGlynn - 21420022
* Joseph McNaney - 21371476
*
* This code is broken up into two parts.
* - Frontend code
* - Backend code
*
* All code functionality is indicated with
*
* ///// Code purpose /////
* [code]
* ///// Code purpose /////
*
* Where code purpose indicates what the code inside is doing
*
*
* Please Enter your OpenAI API key in the provided text box
*
*
* The API calls start at
* //////On Submit Button Click//////
*
**/
var apiKey = "";
//////////////////////////////////Header and Input Row///////////////////////////////////////////////
const header = document.createElement('header');
header.style.backgroundColor = '#333';
header.style.padding = '10px';
header.style.margin = '-10px';
header.style.marginBottom = '10px';
header.style.textAlign = 'center';
header.style.color = 'white';
header.style.fontSize = '30px';
header.appendChild(document.createElement('h1').appendChild(document.createTextNode('DEB.ai.TE')));
const apiInput = document.createElement('input');
apiInput.type = 'text';
apiInput.minLength = '5';
apiInput.maxLength = '500';
apiInput.size = '70';
apiInput.placeholder = 'Enter API key...';
const apiInputButton = document.createElement('button');
apiInputButton.type = 'button';
apiInputButton.style.marginLeft = '10px';
apiInputButton.appendChild(document.createTextNode('Submit API key'));
const apiRow = document.createElement('div');
apiRow.style.display = 'flex';
apiRow.style.justifyContent = 'center';
apiRow.style.padding = '10px';
apiRow.appendChild(apiInput);
apiRow.appendChild(apiInputButton);
const input = document.createElement('input');
input.type = 'text';
input.minLength = '5';
input.maxLength = '500';
input.size = '70';
input.placeholder = 'Enter prompt...';
const inputButton = document.createElement('button');
inputButton.type = 'button';
inputButton.style.marginLeft = '10px';
inputButton.appendChild(document.createTextNode('Send Statement'));
const inputRow = document.createElement('div');
inputRow.style.display = 'flex';
inputRow.style.justifyContent = 'center';
inputRow.style.padding = '10px';
inputRow.appendChild(input);
inputRow.appendChild(inputButton);
//////////////////////////////////Header and Input Row///////////////////////////////////////////////
const main = document.createElement('main');
main.style.display = 'flex';
main.style.justifyContent = 'space-around';
main.style.padding = '20px';
/////////////////////////////Center Columns///////////////////////////////////////////////////////////
// Set style for center columns
function setCenterColumnStyle(column) {
column.style.flex = '1';
column.style.padding = '10px';
column.style.border = '1px solid #ddd';
column.style.margin = '10px';
column.style.minHeight = '600px';
column.style.maxWidth = '30%';
}
// Create "For" column
const column1 = document.createElement('div');
column1.className = 'column';
column1.id = 'forColumn';
setCenterColumnStyle(column1);
const forText = document.createElement('div');
forText.id = 'forText';
forText.appendChild(document.createElement('p'));
column1.appendChild(document.createElement('h2'));
column1.querySelector('h2').innerHTML = 'Agree';
column1.appendChild(forText);
// Create "Against" column
const column2 = document.createElement('div');
column2.className = 'column';
column2.id = 'againstColumn';
setCenterColumnStyle(column2);
const againstText = document.createElement('div');
againstText.id = 'againstText';
againstText.appendChild(document.createElement('p'));
column2.appendChild(document.createElement('h2'));
column2.querySelector('h2').innerHTML = 'Disagree';
column2.appendChild(againstText);
/////////////////////////////Center Columns///////////////////////////////////////////////////////////
/////////////////////////////Side Columns///////////////////////////////////////////////////////////
function setSideColumnStyle(column) {
column.style.flex = '0.5';
column.style.padding = '10px';
column.style.border = '1px solid #ddd';
column.style.margin = '10px';
}
const factCheckColumn = document.createElement('div');
factCheckColumn.className = 'column';
factCheckColumn.id = 'factCheckColumn';
setSideColumnStyle(factCheckColumn);
factCheckColumn.appendChild(document.createElement('h3'));
factCheckColumn.querySelector('h3').innerHTML = 'Fact Check Column';
const dictionaryColumn = document.createElement('div');
dictionaryColumn.className = 'column';
dictionaryColumn.id = 'dictionaryColumn';
dictionaryColumn.style.flex = '0.5';
dictionaryColumn.style.padding = '10px';
dictionaryColumn.style.border = '1px solid #ddd';
dictionaryColumn.style.margin = '10px';
setSideColumnStyle(dictionaryColumn);
dictionaryColumn.appendChild(document.createElement('h3'));
dictionaryColumn.querySelector('h3').innerHTML = 'Dictionary Column';
/////////////////////////////Side Columns///////////////////////////////////////////////////////////
/////////////////////////Hover Text Style////////////////////////////////////////////////////
// Add event listener "Mouseover" to the text
forText.addEventListener("mouseover", hoverEffect);
againstText.addEventListener('mouseover', hoverEffect);
// Add event listener "Mouseout"to the text
forText.addEventListener("mouseout", undoHoverEffect);
againstText.addEventListener('mouseout', undoHoverEffect);
function hoverEffect(event) {
event.target.style.cursor = 'pointer';
if (event.target.classList.contains('clickable')) {
event.target.style.textDecoration = 'underline';
}
}
function undoHoverEffect(event) {
if (event.target.classList.contains('clickable')) {
event.target.style.textDecoration = 'none';
}
}
/////////////////////////Hover Text Style////////////////////////////////////////////////////
main.appendChild(factCheckColumn);
main.appendChild(column1);
main.appendChild(column2);
main.appendChild(dictionaryColumn);
document.body.appendChild(header);
document.body.appendChild(apiRow);
document.body.appendChild(inputRow);
document.body.appendChild(main);
/////////////////////////On Submit api Click////////////////////////////////////////////////////
apiInputButton.addEventListener('mouseup', submitAPIKey);
function submitAPIKey() {
apiKey = apiInput.value;
console.log(apiKey);
apiRow.innerHTML = "<h5> API Key Entered </h5>";
}
/////////////////////////On Submit Button Click////////////////////////////////////////////////////
inputButton.addEventListener('mouseup', updateColumns);
// Clear center columns
function clearCenterColumns() {
forText.innerHTML = 'loading...';
againstText.innerHTML = 'loading...';
}
// change to add input from gpt api call
function updateColumns() {
// Clear columns before adding anything
clearCenterColumns();
if (apiKey == "") {
forText.innerHTML = "please enter APIkey";
againstText.innerHTML = "please enter APIKey";
return false;
}
const openaiEndpoint = 'https://api.openai.com/v1/chat/completions';
const themodel = 'gpt-3.5-turbo';
theprompt = input.value;
// Agree AI
var forData = {
"model": themodel,
"temperature": 0.7,
"messages": [
{
"role": "user",
"content": theprompt
},
{
"role": "system",
"content": "You are part of a debate team. You are to play the role of promoter of faith. You must agree with every prompt for the sake of the debate. Do not mention that you are playing promotor of faith. Simply agree with the prompt. Give reasons."
}
]
};
// Disagree AI
var againstData = {
"model": themodel,
"temperature": 0.7,
"messages": [
{
"role": "user",
"content": theprompt
},
{
"role": "system",
"content": "You are part of a debate team. You are to play the role of devil's advocate. You must disagree with every prompt for the sake of the debate. Do not mention that you are playing devil's advocate. Simply disagree with the prompt. Give reasons."
}
]
};
// MH edit
console.log ( theprompt );
console.log ( "You are part of a debate team. You are to play the role of promoter of faith. You must agree with every prompt for the sake of the debate. Do not mention that you are playing promotor of faith. Simply agree with the prompt. Give reasons." );
console.log ( "You are part of a debate team. You are to play the role of devil's advocate. You must disagree with every prompt for the sake of the debate. Do not mention that you are playing devil's advocate. Simply disagree with the prompt. Give reasons." );
// Fetch the Agree AI response
function forPrompt() {
fetch(openaiEndpoint, {
method: "POST",
headers:
{
"Content-Type": "application/json",
"Authorization": "Bearer " + apiKey
},
body: JSON.stringify(forData)
})
.then(response => response.json())
.then(data => {
console.log(data);
var theresponse = data.choices[0].message.content;
console.log(theresponse);
const inputString = theresponse.split(' ');
// Remove loading text
forText.innerHTML = '';
inputString.map( (word) => {
forText.innerHTML += `<span class="clickable">${word} </span>`;
})
})
.catch((error) => {
console.error('Error:', error);
forText.innerHTML = `Something went wrong`;
});
}
// Fetch the Disagree AI response
function againstPrompt() {
fetch(openaiEndpoint, {
method: "POST",
headers:
{
"Content-Type": "application/json",
"Authorization": "Bearer " + apiKey
},
body: JSON.stringify(againstData)
})
.then(response => response.json())
.then(data => {
console.log(data);
var theresponse = data.choices[0].message.content;
console.log(theresponse);
const inputString = theresponse.split(' ');
// Remove loading text
againstText.innerHTML = '';
inputString.map( (word) => {
againstText.innerHTML += `<span class="clickable">${word} </span>`;
})
})
.catch((error) => {
console.error('Error:', error);
againstText.innerHTML = `Something went wrong`;
});
}
forPrompt();
againstPrompt();
// This is needed so the fucntion waits for the API call to finish
setInterval(() => {}, 2000);
}
/////////////////////////On Submit Button Click////////////////////////////////////////////////////
////////////////////////////Dictionary API Call///////////////////////////////////////////////
forText.addEventListener('click', getDefinition);
againstText.addEventListener('click', getDefinition);
function getDefinition(event) {
// Check if the clicked element has the "clickable" class
if (event.target.classList.contains('clickable')) {
// Call the dictionary API with the clicked word
const word = event.target.innerText;
const wordCleaned = word.replace(/[^a-zA-Z]/g, '');
var apiUrl = `https://api.dictionaryapi.dev/api/v2/entries/en/${wordCleaned}`;
dictionaryColumn.innerHTML = '';
// Fetch the API response
fetch(apiUrl)
.then(response => response.json())
.then(data => {
// Check if the API response is an array and not empty
if (Array.isArray(data) && data.length > 0) {
showDefinition(data); // Display the definitions in the column
}
else {
dictionaryColumn.innerHTML += `<p>No definitions found for the word '${wordCleaned}'.</p>`;
}
})
.catch(error => {
console.error('Error fetching API:', error);
});
}
};
function showDefinition(data) {
// Iterate through each entry and print definitions
data.forEach(entry => {
dictionaryColumn.innerHTML += `<h4>Word: ${entry.word}</h4>`;
entry.meanings.forEach(meaning => {
dictionaryColumn.innerHTML += `<h5>Part of Speech: ${meaning.partOfSpeech}</h5>`;
meaning.definitions.slice(0,4).forEach(definition => {
dictionaryColumn.innerHTML += `<p>Definition: ${definition.definition}</p>`;
});
});
dictionaryColumn.innerHTML += `<br>`;
});
};
////////////////////////////Dictionary API Call///////////////////////////////////////////////
////////////////////////////Fact Check API Call///////////////////////////////////////////////
var wikiCheckEndpoint = "https://nli.wmflabs.org/fact_checking_aggregated/?claim=";
column1.addEventListener('mouseup', getFactCheck);
column2.addEventListener('mouseup', getFactCheck);
function getFactCheck() {
var highlightedText = window.getSelection().toString();
if (highlightedText != "") {
factCheckColumn.innerHTML = '';
factCheckColumn.innerHTML = `<h3>Fact Checking: <p> ${highlightedText} </p> </h3>`;
factCheckColumn.querySelector('p').style.fontWeight = 'normal';
factCheckColumn.appendChild(document.createElement('h5'));
factCheckColumn.querySelector('h5').innerHTML = `<h5>Loading...</h5>`;
callWikiCheckAPI(highlightedText);
}
}
function callWikiCheckAPI(claim) {
// Clean claim to be checked
claimCleaned = claim.replace(/ /g, '%20');
// Fetch the API response
fetch(wikiCheckEndpoint + claimCleaned, {
method: "GET",
headers:
{
"Content-Type": "application/json",
},
})
.then(response => response.json())
.then(data => {
console.log(data);
// Inconclusive result
if (data.predicted_label.match("NOT ENOUGH INFO")){
factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Inconclusive</h5>`;
factCheckColumn.innerHTML += `<p>Not enough info</p>`;
}
// Factual result
else if (data.predicted_label.match("SUPPORTS")){
factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Factual</h5>`;
printSupportingEvidence(data.predicted_evidence);
}
// Inaccurate result
else if (data.predicted_label.match("REFUTES")){
factCheckColumn.querySelector('h5').innerHTML = `<h5>Result: Inaccurate</h5>`;
printSupportingEvidence(data.predicted_evidence);
}
})
// Incase of error
.catch(error => {
console.error('Error fetching API:', error);
factCheckColumn.querySelector('h5').innerHTML = `<h5>ERROR OBTAINING API RESULT</h5>`;
});
// This is needed so the fucntion waits for the API call to finish
setTimeout(() => {}, 1000);
}
function printSupportingEvidence(data) {
factCheckColumn.innerHTML += `<h4>Supporting Evidence</h4>`;
data.forEach(evidence => {
factCheckColumn.innerHTML += `<h4> - Article Title: ${evidence[0].replace(/_/g, " ")} </h4>`;
factCheckColumn.innerHTML += `<p> ${evidence[1]}</p>`;
});
}
////////////////////////////Fact Check API Call///////////////////////////////////////////////