Add direct download for session/chat history JSONs

This commit is contained in:
oobabooga 2023-08-02 18:50:13 -07:00
parent 32a2bbee4a
commit 0e8f9354b5
5 changed files with 66 additions and 63 deletions

View file

@ -17,6 +17,7 @@ main_parent.addEventListener('click', function(e) {
}
});
// Add some scrollbars
const textareaElements = document.querySelectorAll('.add_scrollbar textarea');
for(i = 0; i < textareaElements.length; i++) {
textareaElements[i].classList.remove('scroll-hide');

40
css/save_files.js Normal file
View file

@ -0,0 +1,40 @@
// Functions for downloading JSON files
function getCurrentTimestamp() {
const now = new Date();
const timezoneOffset = now.getTimezoneOffset() * 60000; // Convert to milliseconds
const localTime = new Date(now.getTime() - timezoneOffset);
const formattedTimestamp = localTime.toISOString().replace(/[-:]/g, '').slice(0, 15);
return formattedTimestamp;
}
function saveFile(contents, filename) {
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contents));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function saveHistory(history, character, mode) {
let path = null;
if (['chat', 'chat-instruct'].includes(mode) && character && character.trim() !== '') {
path = `history_${character}_${getCurrentTimestamp()}.json`;
} else {
try {
path = `history_${mode}_${getCurrentTimestamp()}.json`;
} catch (error) {
path = `history_${getCurrentTimestamp()}.json`;
}
}
saveFile(history, path);
}
function saveSession(session, mode) {
let path = null;
path = `session_${mode}_${getCurrentTimestamp()}.json`;
saveFile(session, path);
}

View file

@ -412,23 +412,6 @@ def load_history(file, history):
return history
def save_history_at_user_request(history, character, mode):
def make_timestamp_path(character=None):
return f"logs/{character or ''}{'_' if character else ''}{datetime.now().strftime('%Y%m%d-%H%M%S')}.json"
path = None
if mode in ['chat', 'chat-instruct'] and character not in ['', 'None', None]:
path = make_timestamp_path(character)
else:
# Try to use mode as the file name, otherwise just use the timestamp
try:
path = make_timestamp_path(mode.capitalize())
except:
path = make_timestamp_path()
return save_history(history, path)
def save_persistent_history(history, character, mode):
if mode in ['chat', 'chat-instruct'] and character not in ['', 'None', None] and not shared.args.multi_user:
save_history(history, path=Path(f'logs/{character}_persistent.json'))

View file

@ -15,6 +15,8 @@ with open(Path(__file__).resolve().parent / '../css/main.js', 'r') as f:
main_js = f.read()
with open(Path(__file__).resolve().parent / '../css/chat.js', 'r') as f:
chat_js = f.read()
with open(Path(__file__).resolve().parent / '../css/save_files.js', 'r') as f:
save_files_js = f.read()
refresh_symbol = '🔄'
delete_symbol = '🗑️'
@ -145,9 +147,6 @@ def gather_interface_values(*args):
if not shared.args.multi_user:
shared.persistent_interface_state = output
Path('logs').mkdir(exist_ok=True)
with open(Path(f'logs/session_{shared.get_mode()}_autosave.json'), 'w') as f:
f.write(json.dumps(output, indent=4))
return output

View file

@ -508,44 +508,24 @@ def create_file_saving_event_handlers():
if not shared.args.multi_user:
def load_session(session, state):
with open(Path(f'logs/{session}.json'), 'r') as f:
state.update(json.loads(f.read()))
def load_session(file, state):
decoded_file = file if type(file) == str else file.decode('utf-8')
data = json.loads(decoded_file)
state.update(data)
if shared.is_chat():
chat.save_persistent_history(state['history'], state['character_menu'], state['mode'])
return state
if shared.is_chat():
shared.gradio['save_session'].click(
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
lambda x: json.dumps(x, indent=4), gradio('interface_state'), gradio('save_contents')).then(
lambda: 'logs/', None, gradio('save_root')).then(
lambda x: f'session_{shared.get_mode()}_{x + "_" if x not in ["None", None, ""] else ""}{utils.current_time()}.json', gradio('character_menu'), gradio('save_filename')).then(
lambda: gr.update(visible=True), None, gradio('file_saver'))
shared.gradio['save_session'].click(
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
lambda x: json.dumps(x, indent=4), gradio('interface_state'), gradio('temporary_text')).then(
None, gradio('temporary_text'), None, _js=f"(contents) => {{{ui.save_files_js}; saveSession(contents, \"{shared.get_mode()}\")}}")
shared.gradio['session_menu'].change(
load_session, gradio('session_menu', 'interface_state'), gradio('interface_state')).then(
ui.apply_interface_values, gradio('interface_state'), gradio(ui.list_interface_input_elements()), show_progress=False).then(
chat.redraw_html, shared.reload_inputs, gradio('display'))
else:
shared.gradio['save_session'].click(
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
lambda x: json.dumps(x, indent=4), gradio('interface_state'), gradio('save_contents')).then(
lambda: 'logs/', None, gradio('save_root')).then(
lambda: f'session_{shared.get_mode()}_{utils.current_time()}.json', None, gradio('save_filename')).then(
lambda: gr.update(visible=True), None, gradio('file_saver'))
shared.gradio['session_menu'].change(
load_session, gradio('session_menu', 'interface_state'), gradio('interface_state')).then(
ui.apply_interface_values, gradio('interface_state'), gradio(ui.list_interface_input_elements()), show_progress=False)
shared.gradio['delete_session'].click(
lambda x: f'{x}.json', gradio('session_menu'), gradio('delete_filename')).then(
lambda: 'logs/', None, gradio('delete_root')).then(
lambda: gr.update(visible=True), None, gradio('file_deleter'))
shared.gradio['load_session'].upload(
load_session, gradio('load_session', 'interface_state'), gradio('interface_state')).then(
ui.apply_interface_values, gradio('interface_state'), gradio(ui.list_interface_input_elements()), show_progress=False)
def set_interface_arguments(interface_mode, extensions, bool_active):
@ -558,7 +538,6 @@ def set_interface_arguments(interface_mode, extensions, bool_active):
setattr(shared.args, k, False)
if interface_mode != "default":
setattr(shared.args, interface_mode, True)
for k in bool_list:
setattr(shared.args, k, False)
for k in bool_active:
@ -622,6 +601,9 @@ def create_interface():
# Floating menus for saving/deleting files
create_file_saving_menus()
# Used for saving files using javascript
shared.gradio['temporary_text'] = gr.Textbox(visible=False)
# Create chat mode interface
if shared.is_chat():
shared.input_elements = ui.list_interface_input_elements()
@ -702,11 +684,10 @@ def create_interface():
with gr.Tab('Chat history'):
with gr.Row():
with gr.Column():
shared.gradio['download'] = gr.File(label="Download")
shared.gradio['download_button'] = gr.Button(value='Refresh')
shared.gradio['save_chat_history'] = gr.Button(value='Save history')
with gr.Column():
shared.gradio['upload_chat_history'] = gr.File(type='binary', file_types=['.json', '.txt'], label="Upload")
shared.gradio['load_chat_history'] = gr.File(type='binary', file_types=['.json', '.txt'], label="Upload History JSON")
with gr.Tab('Upload character'):
with gr.Tab('YAML or JSON'):
@ -845,11 +826,8 @@ def create_interface():
with gr.Column():
if not shared.args.multi_user:
with gr.Row():
shared.gradio['session_menu'] = gr.Dropdown(choices=utils.get_available_sessions(), value='None', label='Session', elem_classes='slim-dropdown', info='When saving a session, make sure to keep the initial part of the filename (session_chat, session_notebook, or session_default), otherwise it will not appear on this list afterwards.')
ui.create_refresh_button(shared.gradio['session_menu'], lambda: None, lambda: {'choices': utils.get_available_sessions()}, ['refresh-button'])
shared.gradio['save_session'] = gr.Button('💾', elem_classes=['refresh-button'])
shared.gradio['delete_session'] = gr.Button('🗑️', elem_classes=['refresh-button'])
shared.gradio['save_session'] = gr.Button('Save session')
shared.gradio['load_session'] = gr.File(type='binary', file_types=['.json'], label="Upload Session JSON")
extension_name = gr.Textbox(lines=1, label='Install or update an extension', info='Enter the GitHub URL below and press Enter. For a list of extensions, see: https://github.com/oobabooga/text-generation-webui-extensions ⚠️ WARNING ⚠️ : extensions can execute arbitrary code. Make sure to inspect their source code before activating them.')
extension_status = gr.Markdown()
@ -967,8 +945,8 @@ def create_interface():
shared.gradio['instruction_template'].change(
partial(chat.load_character, instruct=True), gradio('instruction_template', 'name1_instruct', 'name2_instruct'), gradio('name1_instruct', 'name2_instruct', 'dummy', 'dummy', 'context_instruct', 'turn_template'))
shared.gradio['upload_chat_history'].upload(
chat.load_history, gradio('upload_chat_history', 'history'), gradio('history')).then(
shared.gradio['load_chat_history'].upload(
chat.load_history, gradio('load_chat_history', 'history'), gradio('history')).then(
chat.redraw_html, shared.reload_inputs, gradio('display'))
shared.gradio['Copy last reply'].click(chat.send_last_reply_to_input, gradio('history'), gradio('textbox'), show_progress=False)
@ -991,7 +969,9 @@ def create_interface():
lambda: 'characters/instruction-following/', None, gradio('delete_root')).then(
lambda: gr.update(visible=True), None, gradio('file_deleter'))
shared.gradio['download_button'].click(chat.save_history_at_user_request, gradio('history', 'character_menu', 'mode'), gradio('download'))
shared.gradio['save_chat_history'].click(lambda x: json.dumps(x, indent=4), gradio('history'), gradio('temporary_text')).then(
None, gradio('temporary_text', 'character_menu', 'mode'), None, _js=f"(hist, char, mode) => {{{ui.save_files_js}; saveHistory(hist, char, mode)}}")
shared.gradio['Submit character'].click(chat.upload_character, gradio('upload_json', 'upload_img_bot'), gradio('character_menu'))
shared.gradio['upload_json'].upload(lambda: gr.update(interactive=True), None, gradio('Submit character'))
shared.gradio['upload_json'].clear(lambda: gr.update(interactive=False), None, gradio('Submit character'))