Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Toast, GUI update, better form handling, adding JSON viewer #5

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
156 changes: 66 additions & 90 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,106 +1,82 @@
from flask import Flask, render_template, request, redirect, url_for
from flask import Flask, render_template, request, redirect, url_for, jsonify
import json, os

app = Flask(__name__)

def clean_entry(entry):
entry = entry.strip().replace("\r", "").replace(" \n", "\n")
return entry
return entry.strip().replace("\r", "").replace(" \n", "\n")

# Route for index/main page
@app.route('/', defaults={'active_tab': 'sft'})
@app.route('/<active_tab>')
def index(active_tab):
return render_template('index.html', active_tab=active_tab)

# Route for the SFT Dataset Builder.
@app.route('/sft', methods=['GET', 'POST'])
@app.route('/sft', methods=['POST'])
def form():
if request.method == 'POST':
# Extract form data
system_prompt = request.form.get('system')
user_prompts = request.form.getlist('user[]')
gpt_responses = request.form.getlist('gpt[]')

# Clean the system prompt, user prompts, and gpt responses
system_prompt = clean_entry(system_prompt)
user_prompts = [clean_entry(prompt) for prompt in user_prompts]
gpt_responses = [clean_entry(response) for response in gpt_responses]

# Data to be appended
data_to_append = {
'conversations': [
{
'from': 'system',
'value': system_prompt
}
],
'source': 'manual'
}

# Add turns to the conversation
for user_prompt, gpt_response in zip(user_prompts, gpt_responses):
data_to_append['conversations'].append({
'from': 'human',
'value': user_prompt
})
data_to_append['conversations'].append({
'from': 'gpt',
'value': gpt_response
})

# File path
file_path = './sft_data.json'

# Check if file exists and append data
if os.path.exists(file_path):
with open(file_path, 'r+', encoding='utf-8') as file:
data = json.load(file)
data.append(data_to_append)
file.seek(0)
json.dump(data, file, indent=4)
else:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump([data_to_append], file, indent=4)

return redirect(url_for('index'))
return redirect(url_for('index'))

# Route for the DPO dataset builder
@app.route('/dpo', methods=['GET', 'POST'])
system_prompt = clean_entry(request.form.get('system'))
user_prompts = [clean_entry(prompt) for prompt in request.form.getlist('user[]')]
gpt_responses = [clean_entry(response) for response in request.form.getlist('gpt[]')]

data_to_append = {
'conversations': [{'from': 'system', 'value': system_prompt}],
'source': 'manual'
}

for user_prompt, gpt_response in zip(user_prompts, gpt_responses):
data_to_append['conversations'].extend([
{'from': 'human', 'value': user_prompt},
{'from': 'gpt', 'value': gpt_response}
])

addJsonData('./sft_data.json', data_to_append)

return redirect(url_for('index', active_tab='sft'))

@app.route('/dpo', methods=['POST'])
def dpo_form():
if request.method == 'POST':
# Extract form data
system_prompt = request.form.get('system')
prompt = request.form.get('prompt')
chosen = request.form.get('chosen')
rejected = request.form.get('rejected')

# Data to be appended
data_to_append = {
'system': clean_entry(system_prompt),
'question': clean_entry(prompt),
system_prompt = clean_entry(request.form.get('system'))
prompts = request.form.getlist('prompt')
chosen_responses = request.form.getlist('chosen')
rejected_responses = request.form.getlist('rejected')

data_to_append = {
'system': system_prompt,
'conversations': [],
'source': 'manual'
}

for prompt, chosen, rejected in zip(prompts, chosen_responses, rejected_responses):
data_to_append['conversations'].append({
'prompt': clean_entry(prompt),
'chosen': clean_entry(chosen),
'rejected': clean_entry(rejected),
'source': 'manual'
}

# File path
file_path = './dpo_data.json'

# Check if file exists and append data
if os.path.exists(file_path):
with open(file_path, 'r+', encoding='utf-8') as file:
data = json.load(file)
data.append(data_to_append)
file.seek(0)
json.dump(data, file, indent=4)
else:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump([data_to_append], file, indent=4)

return "Success", 200
return render_template('index.html', active_tab='dpo')
'rejected': clean_entry(rejected)
})

addJsonData('./dpo_data.json', data_to_append)

return "Success", 200

def addJsonData(file_path, data_to_append):
if os.path.exists(file_path):
with open(file_path, 'r+', encoding='utf-8') as file:
data = json.load(file)
data.append(data_to_append)
file.seek(0)
json.dump(data, file, indent=4)
else:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump([data_to_append], file, indent=4)

@app.route('/<type>_data.json', methods=['GET', 'PUT'])
def data(type):
file_path = f'./{type}_data.json'
if request.method == 'GET':
with open(file_path, 'r', encoding='utf-8') as file:
return jsonify(json.load(file))
else:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(request.get_json(), file, indent=4, sort_keys=False)
return jsonify({'message': f'{type.upper()} data updated successfully'})

if __name__ == '__main__':
app.run(debug=True, port=7272)
app.run(debug=True, port=7272)
171 changes: 171 additions & 0 deletions static/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
function showToast(message, type) {
const toast = document.getElementById('toast');
toast.className = `toast ${type}`;
toast.innerText = message;
toast.style.display = 'block';
setTimeout(() => {
toast.style.display = 'none';
}, 3000);
}

function submitForm(e, tabName) {
e.preventDefault();
const form = document.getElementById(tabName === 'sft' ? 'sftForm' : 'dpoForm');
const formElements = form.elements;
const allFieldsFilled = Array.from(formElements).every(el => {
if (el.tagName === 'TEXTAREA') {
if (el.id === 'system') {
return true;
} else {
return el.value.trim() !== '';
}
}
return true;
});

const turns = document.getElementById(tabName + 'Turns');
const turnElements = turns.getElementsByClassName('turn');
if (turnElements.length === 0) {
showToast("Please add at least one turn.", 'error');
return;
}

if (allFieldsFilled) {
fetch(form.action, {
method: form.method,
body: new FormData(form)
})
.then(response => {
if (response.ok) {
resetForm(tabName);
openJsonModal(tabName);
showToast("Submitted successfully!", 'success');
} else {
showToast("Form submission failed.", 'error');
}
})
.catch(error => console.error('Error:', error));
} else {
showToast("Fill all the fields.", 'error');
}
}

function resetForm(formType) {
const form = document.getElementById(formType === 'sft' ? 'sftForm' : 'dpoForm');
const turns = document.getElementById(formType + 'Turns');
const turnElements = turns.getElementsByClassName('turn');

form.reset()
while (turnElements.length > 1) {
turnElements[turnElements.length - 1].remove();
}

showToast("Form Resetted", 'success')
}

function openTab(tabName) {
const tabcontent = document.getElementsByClassName("tabcontent");
Array.from(tabcontent).forEach(el => el.style.display = "none");

const tablinks = document.getElementsByClassName("tablink");
Array.from(tablinks).forEach(el => el.className = el.className.replace(" active", ""));

document.getElementById(tabName).style.display = "block";
const buttonElement = document.querySelector(`.tablink[onclick="openTab('${tabName}', this)"]`);
if (buttonElement) {
buttonElement.className += " active";
}

openJsonModal(tabName);
}

document.getElementById("defaultOpen").click();

function addTurn(formType) {
const turns = document.getElementById(formType + 'Turns');
const newTurn = document.createElement('div');
newTurn.classList.add("turn");

if (formType === 'sft') {
newTurn.innerHTML = `
<div>
<label for="user">User:</label>
<textarea id="user" name="user[]"></textarea>
</div>
<div>
<label for="gpt">GPT:</label>
<textarea id="gpt" name="gpt[]"></textarea>
</div>
<button type="button" class="delete-turn" onclick="deleteTurn(this)">Delete</button>
`;
} else {
newTurn.innerHTML = `
<div>
<label for="prompt">Prompt:</label>
<textarea id="prompt" name="prompt"></textarea>
</div>
<div>
<label for="chosen">Chosen:</label>
<textarea id="chosen" name="chosen"></textarea>
</div>
<div>
<label for="rejected">Rejected:</label>
<textarea id="rejected" name="rejected"></textarea>
</div>
<button type="button" class="delete-turn" onclick="deleteTurn(this)">Delete</button>
`;
}

turns.appendChild(newTurn);
turns.scrollTop = turns.scrollHeight;
}

function deleteTurn(button) {
const turn = button.closest('.turn');
turn.remove();
}

async function openJsonModal(type) {
const jsonViewer = document.getElementById('jsonViewer');
const editJsonBtn = document.getElementById('editJsonBtn');
const saveJsonBtn = document.getElementById('saveJsonBtn');

const propertyOrder = type === 'dpo' ? ['system', 'conversations', 'prompt', 'chosen', 'rejected', 'source'] : ['conversations', 'from', 'value', 'source'];

try {
const response = await fetch(`/${type}_data.json`);
const data = await response.json();
jsonViewer.textContent = JSON.stringify(data, propertyOrder, 2);
hljs.highlightElement(jsonViewer);

editJsonBtn.onclick = () => {
jsonViewer.contentEditable = true;
jsonViewer.focus();
editJsonBtn.style.display = 'none';
saveJsonBtn.style.display = 'inline-block';
};

saveJsonBtn.onclick = async () => {
try {
const updatedData = JSON.parse(jsonViewer.textContent);
await fetch(`/${type}_data.json`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updatedData, propertyOrder, 2)
});
jsonViewer.contentEditable = false;
editJsonBtn.style.display = 'inline-block';
saveJsonBtn.style.display = 'none';
showToast('JSON data updated successfully!', 'success');
} catch (error) {
console.error('Error updating JSON data:', error);
showToast('Failed to update JSON data.', 'error');
}
};
} catch (error) {
console.error('Error fetching JSON data:', error);
showToast('Failed to fetch JSON data.', 'error');
}
}
Loading