/*
Module: CA318 or CSC1047 Advanced Algorithms & Ai search
Assignment: Testing Multiple Ai Apis 2024 (40%)
Professor: Mark Humphry
Paired Project: Yes
Student 1: Diana Williams Oshun - Student No: 23101961
Student 2: Hind Mutasim Mohamed - Student No: 22355046
Date: 19/11/2024
Description: This program is designed to make show accurate Harry Potter fanfictions, it will output two generated fanfictions
and the user will vote on which fanfiction they liked better. The program works by collecting harry potter information from the
harry potter api PotterDB, this information along with information about the user's original character (name, age, height, love
interest, wizard status) and a short description of what they want to happen in the fanfiction, are combined to make a fanfiction
prompt. This prompt is given to two different Ai apis, Cohere and TextCortex. The response from these ai's are collecfed and
output to the user. The user then has the option to vote on which fanfic they perfer. A vote count is displayed after they place
their vote.
Bibliography:
[1]“REST API - Potter DB: Docs,” Potterdb.com, 2023. https://docs.potterdb.com/apis/rest
[2]“Make Integration,” TextCortex Help Center, 2024.
https://help.textcortex.com/hc/en-us/articles/21646438590737-Make-Integration
[3]“TextCortex API Docs - Generate Text, Code, Summarize, Expand, Paraphrase,” Textcortex.com, 2024.
https://docs.textcortex.com/api/paths/texts-expansions/post
[4]“Generate (v1 API) — Cohere,” Cohere, 2024. https://docs.cohere.com/v1/reference/generate
[5]“Chat — Cohere,” Cohere, 2024. https://docs.cohere.com/reference/chat
[6] Chat with ChatGPT, Ancientbrain.com, 2024. https://ancientbrain.com/world.php?world=0197248368
Api Keys needed to run:
Cohere Api key:F94QY1cxxkLuYIM3Tm99Tuqudpj2YhE3V0Xcdc92
TextCortex Api key:gAAAAABnPjRAV60WGTmO5imFVAwg0RVOPBhXD3dVvlQvcZ5c3dQTAurEZLxrrGqZxWgXjdiwQvP1cOdu4fJHRVfNF8NRte8ir9XoywRzToZVSn8XQEvYXejl59Uajh7kINy47cD6HdXf
***Note: We are on the free trial version of them, we are unaware of when the trial amount of generation will run out. In the
case that one of the API do run out of generations, please contact us and we will provide a new API key for the project.
Work Devision:
Diana Williams Oshun -> Voting boxes, TextCortex implemtation, getting information from PotterDB api, partial fanfic input box,
fanfic prompt creation.
Hind Mutasim Mohamed -> Fanfic output boxes, Cohere implemtation, page layout and style, fanfic input box.
Throughout the code you will find comments of either 'Diana' or 'Hind' this means that particular section of code was written by
that individual.
*/
/*
Hind
This controlls the overall page style
*/
document.body.style = `
display: flex;
flex-direction: column;
align-items: center;
background: url(/uploads/willid2/123.png) repeat;
background-size: 200px 200px;
font-family: Times New Roman;
padding: 20px;
`;
/*
Hind
This box allows us to input details about the orginal character that the user comes up with along with their description for the fanfic they want
*/
// Fanfic Input Box
const inputBox = document.createElement("div");
inputBox.style = "background-color: white; padding: 20px; width: 600px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); margin-bottom: 20px;";
inputBox.innerHTML = `
<h1>Harry Potter Fanfictions</h1>
<h3>Create Your Fanfic</h3>
<label>Characters:</label><br>
<input id="characters" placeholder="Harry, Hermione, OC" style="width: 100%; margin-bottom: 10px;"><br>
<label>Original Character Details:</label><br>
Name: <input id="ocName" style="width: 100%;"><br>
Age: <input id="ocAge" style="width: 100%;"><br>
Height: <input id="ocHeight" style="width: 100%;"><br>
Wizard: <input type="checkbox" id="ocWizard"><br>
In Love With: <input id="ocLoveInterest" style="width: 100%;"><br><br>
<!--
Diana
This section is so the books are taken from the potter db api
-->
<label>Fanfic Settings:</label><br>
Based on Book: <select id="bookSelect" style="width: 100%; margin-bottom: 10px;">
<option value="">Select Book Setting</option>
</select><br>
Genre: <input id="genre" placeholder="Adventure, Romance" style="width: 100%;"><br>
Love Interests: <input id="loveInterests" style="width: 100%;"><br>
Description: <textarea id="fanficDescription" placeholder="Describe your fanfic" style="width: 100%; height: 60px;"></textarea><br>
<label>API Keys:</label><br>
Cohere API Key: <input id="cohereApiKey" style="width: 100%;"><br>
TextCortex API Key: <input id="textCortexApiKey" style="width: 100%;"><br><br>
<button onclick="generateFanfic()">Generate Story</button>
<div id="statusText" style="margin-top: 10px;"></div>
`;
document.body.appendChild(inputBox);
/*
Hind
Output Boxes for fanfic generated by Cohere and TextCortex
*/
const outputBoxCohere = document.createElement("div");
outputBoxCohere.style = "background-color: white; padding: 20px; width: 600px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); margin-top: 20px;";
outputBoxCohere.innerHTML = `<h3>Cohere Harry Potter Fanfic:</h3><div id="fanficOutputCohere" style="white-space: pre-wrap;"></div>`;
document.body.appendChild(outputBoxCohere);
const outputBoxTextCortex = document.createElement("div");
outputBoxTextCortex.style = "background-color: white; padding: 20px; width: 600px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); margin-top: 20px;";
outputBoxTextCortex.innerHTML = `<h3>TextCortex Harry Potter Fanfic:</h3><div id="fanficOutputTextCortex" style="white-space: pre-wrap;"></div>`;
document.body.appendChild(outputBoxTextCortex);
/*
Diana
voting boxes for the best fanfic,
*/
const votingBox = document.createElement("div");
votingBox.style = "background-color: white; padding: 20px; width: 600px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); margin-top: 20px;";
votingBox.innerHTML = `
<h3>Which Fanfic Do You Like Better?</h3>
<button id="voteFanfic1" style="padding: 10px; background-color: rgb(165, 42, 42); color: white; border: none; border-radius: 5px; cursor: pointer;">Fanfic 1 (Cohere)</button>
<button id="voteFanfic2" style="padding: 10px; background-color: rgb(165, 42, 42); color: white; border: none; border-radius: 5px; cursor: pointer; margin-left: 10px;">Fanfic 2 (TextCortex)</button>
<div id="voteStatus" style="margin-top: 10px; font-weight: bold;"></div>
`;
document.body.appendChild(votingBox);
let voteCountFanfic1 = 0;
let voteCountFanfic2 = 0;
document.getElementById('voteFanfic1').addEventListener('click', () => {
voteCountFanfic1++;
updateVoteStatus();
});
document.getElementById('voteFanfic2').addEventListener('click', () => {
voteCountFanfic2++;
updateVoteStatus();
});
function updateVoteStatus() {
document.getElementById('voteStatus').innerHTML = `Fanfic 1 Votes: ${voteCountFanfic1} | Fanfic 2 Votes: ${voteCountFanfic2}`;
}
/*
Diana
Getting the book data from the potterdb api
*/
const potterDbBaseURL = "https://api.potterdb.com/v1/books";
function populateBookSelect() {
fetch(potterDbBaseURL)
.then(response => response.json())
.then(data => {
const bookSelect = document.getElementById('bookSelect');
data.data.forEach(book => {
const option = document.createElement('option');
option.value = book.id;
option.textContent = book.attributes.title;
bookSelect.appendChild(option);
});
})
.catch(error => {
document.getElementById('statusText').innerHTML = `<span style="color: red;">Error loading books: ${error.message}</span>`;
});
}
/*
Diana and Hind
Send prompts to APIs
*/
function sendToAPIs(prompt, cohereApiKey, textCortexApiKey) {
/*
Hind
Cohere api implementation
*/
fetch("https://api.cohere.com/v1/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${cohereApiKey}`
},
body: JSON.stringify({
prompt: prompt,
model: "command",
max_tokens: 500,
temperature: 0.75,
num_generations: 1
})
})
.then(response => {
if (!response.ok) {
throw new Error(`Cohere API error: ${response.statusText}`);
}
return response.json();
})
.then(data => {
// MH edit
console.log (data);
const output = data.generations?.[0]?.text || "No output available.";
document.getElementById('fanficOutputCohere').textContent = output;
})
.catch(error => {
document.getElementById('fanficOutputCohere').textContent = `Error: ${error.message}`;
});
/*
Diana
TextCortex api call implementation
*/
const textCortexOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${textCortexApiKey}`
},
body: JSON.stringify({
formality: "default",
max_tokens: 2048,
model: "claude-3-haiku",
n: 1,
source_lang: "en",
target_lang: "en",
text: prompt
})
};
fetch('https://api.textcortex.com/v1/texts/expansions', textCortexOptions)
.then(response => {
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
return response.json();
})
.then(data => {
// MH edit
console.log (data);
const output = data.data?.outputs?.[0]?.text || "No output available.";
document.getElementById('fanficOutputTextCortex').textContent = output;
})
.catch(error => {
document.getElementById('fanficOutputTextCortex').textContent = `Error: ${error.message}`;
});
}
/*
Diana
Generate the fanfiction, taking oc info, description and harry potter info
*/
function generateFanfic() {
// MH edit
const cohereApiKey = document.getElementById('cohereApiKey').value.trim();
const textCortexApiKey = document.getElementById('textCortexApiKey').value.trim();
if (!cohereApiKey || !textCortexApiKey) {
document.getElementById('statusText').innerHTML = '<span style="color: red;">Please enter both API keys.</span>';
return;
}
const fanficPrompt = document.getElementById('fanficDescription').value.trim();
const ocDetails = {
name: document.getElementById('ocName').value.trim(),
age: document.getElementById('ocAge').value.trim(),
height: document.getElementById('ocHeight').value.trim(),
isWizard: document.getElementById('ocWizard').checked,
loveInterest: document.getElementById('ocLoveInterest').value.trim(),
};
const selectedBookId = document.getElementById('bookSelect').value;
if (!selectedBookId) {
document.getElementById('statusText').innerHTML = '<span style="color: red;">Please select a book from the dropdown.</span>';
return;
}
const storyPrompt = `Fanfic Prompt:
Based on book: ${document.getElementById('bookSelect').options[document.getElementById('bookSelect').selectedIndex].text}
Genre: ${document.getElementById('genre').value.trim()}
Original Character: ${ocDetails.name}, Age: ${ocDetails.age}, Wizard: ${ocDetails.isWizard ? 'Yes' : 'No'}, Love Interest: ${ocDetails.loveInterest}.
Description: ${fanficPrompt}`;
// MH edit
console.log (storyPrompt);
sendToAPIs(storyPrompt, cohereApiKey, textCortexApiKey);
}
// Initialize book list on page load
populateBookSelect();