mirror of
https://github.com/oobabooga/text-generation-webui.git
synced 2025-06-07 06:06:20 -04:00
Merge fee48eb2a8
into 7f6579ab20
This commit is contained in:
commit
8906f55aa9
7 changed files with 814 additions and 12 deletions
94
css/main.css
94
css/main.css
|
@ -1412,6 +1412,7 @@ strong {
|
|||
color: #07ff07;
|
||||
}
|
||||
|
||||
|
||||
.message-attachments {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -1456,3 +1457,96 @@ strong {
|
|||
.dark .attachment-icon {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
|
||||
/* --- Message Versioning Styles --- */
|
||||
|
||||
.message-versioning-container {
|
||||
position: absolute;
|
||||
bottom: -23px;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.message:hover .message-versioning-container,
|
||||
.user-message:hover .message-versioning-container,
|
||||
.assistant-message:hover .message-versioning-container {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.selected-message .message-versioning-container {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.message-versioning-nav-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.message-versioning-nav-arrow {
|
||||
position: static;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 0;
|
||||
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.message .message-versioning-nav-arrow:not([activated]),
|
||||
.user-message .message-versioning-nav-arrow:not([activated]),
|
||||
.assistant-message .message-versioning-nav-arrow:not([activated]) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.3;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.message .message-versioning-nav-arrow:not([activated]):hover,
|
||||
.user-message .message-versioning-nav-arrow:not([activated]):hover,
|
||||
.assistant-message .message-versioning-nav-arrow:not([activated]):hover {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.message-versioning-nav-pos {
|
||||
font-size: 11px;
|
||||
font-family: monospace;
|
||||
color: rgb(156 163 175); /* Match SVG stroke color */
|
||||
user-select: none;
|
||||
min-width: 25px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.dark .message-versioning-nav-pos {
|
||||
color: rgb(156 163 175); /* Same color for dark mode */
|
||||
}
|
||||
|
||||
/* If you want the other buttons to be visible when a message is selected, uncomment the lines below */
|
||||
.selected-message:not(:hover) .message-versioning-nav-arrow[activated]/*,
|
||||
.selected-message:not(:hover) .footer-button*/ {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.selected-message:not(:hover) .message-versioning-nav-arrow:not([activated]) {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.selected-message:hover .message-versioning-nav-arrow[activated]/*,
|
||||
.selected-message:hover .footer-button*/ {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.selected-message:hover .message-versioning-nav-arrow:not([activated]) {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.message-versioning-container[hidden] {
|
||||
display: none;
|
||||
|
|
|
@ -61,6 +61,8 @@ function removeLastClick() {
|
|||
document.getElementById("Remove-last").click();
|
||||
}
|
||||
|
||||
// === History navigation handled in main.js === //
|
||||
|
||||
function handleMorphdomUpdate(text) {
|
||||
// Track open blocks
|
||||
const openBlocks = new Set();
|
||||
|
|
147
js/main.js
147
js/main.js
|
@ -789,3 +789,150 @@ function createMobileTopBar() {
|
|||
}
|
||||
|
||||
createMobileTopBar();
|
||||
|
||||
|
||||
//------------------------------------------------
|
||||
// Message Versioning Integration
|
||||
//------------------------------------------------
|
||||
|
||||
// --- Message Versioning Variables ---
|
||||
// let versioningSelectedMessageElement = null; // Deprecated due to persistent selection state
|
||||
let selectedMessageHistoryIndex = null;
|
||||
let selectedMessageType = null;
|
||||
|
||||
// --- Message Versioning Helper Functions ---
|
||||
|
||||
// Helper function to get Gradio app root (if needed, otherwise use document)
|
||||
function gradioApp() {
|
||||
const elems = document.querySelectorAll('gradio-app');
|
||||
const gradioShadowRoot = elems.length > 0 ? elems[0].shadowRoot : null;
|
||||
return gradioShadowRoot || document;
|
||||
}
|
||||
|
||||
// Helper to update Gradio text/number inputs (if needed for backend communication)
|
||||
function updateVersioningGradioInput(element, value) {
|
||||
if (element) {
|
||||
element.value = value;
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
} else {
|
||||
console.warn("Attempted to update a null Gradio input element.");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Message Versioning Core Functions ---
|
||||
|
||||
function triggerVersionNavigateBackend(historyIndex, messageType, direction) {
|
||||
console.log(`DEBUG: Triggering version navigate backend with historyIndex: ${historyIndex}, messageType: ${messageType}, direction: ${direction}`);
|
||||
const gradio = gradioApp();
|
||||
const historyIndexInput = gradio.querySelector('#message-versioning-history-index-hidden input[type="number"]');
|
||||
const messageTypeInput = gradio.querySelector('#message-versioning-message-type-hidden input[type="number"]');
|
||||
const directionInput = gradio.querySelector('#message-versioning-direction-hidden textarea');
|
||||
const navigateButton = gradio.querySelector('#message-versioning-navigate-hidden');
|
||||
|
||||
if (historyIndexInput && messageTypeInput && directionInput && navigateButton) {
|
||||
console.log("DEBUG: Found hidden Gradio elements for navigation.");
|
||||
updateVersioningGradioInput(historyIndexInput, historyIndex);
|
||||
updateVersioningGradioInput(messageTypeInput, messageType);
|
||||
updateVersioningGradioInput(directionInput, direction);
|
||||
navigateButton.click();
|
||||
} else {
|
||||
console.error("Message Versioning: Could not find hidden Gradio elements for navigation. Backend communication needs setup.");
|
||||
// Fallback or error handling: Log a warning?
|
||||
}
|
||||
}
|
||||
|
||||
// Function called by the nav arrow's onclick attribute
|
||||
function versioningNavigateClick(arrowButton, historyIndex, messageType, direction) {
|
||||
// Keep the message selected
|
||||
const messageElement = arrowButton.closest('.message, .user-message, .assistant-message');
|
||||
if (messageElement) {
|
||||
versioningSelectMessage(messageElement, historyIndex, messageType);
|
||||
}
|
||||
|
||||
triggerVersionNavigateBackend(historyIndex, messageType, direction);
|
||||
}
|
||||
|
||||
|
||||
function versioningSelectMessage(element, historyIndex, messageType) {
|
||||
// Remove previous selection
|
||||
versioningDeselectMessages();
|
||||
|
||||
if (element) {
|
||||
// versioningSelectedMessageElement = element;
|
||||
selectedMessageHistoryIndex = historyIndex;
|
||||
selectedMessageType = messageType;
|
||||
element.classList.add('selected-message');
|
||||
}
|
||||
}
|
||||
|
||||
function versioningDeselectMessages() {
|
||||
const selectedMessageElement = gradioApp().querySelector('#chat .selected-message');
|
||||
// if (versioningSelectedMessageElement) {
|
||||
// versioningSelectedMessageElement.classList.remove('selected-message');
|
||||
if (selectedMessageElement) {
|
||||
selectedMessageElement.classList.remove('selected-message');
|
||||
}
|
||||
// versioningSelectedMessageElement = null;
|
||||
selectedMessageHistoryIndex = null;
|
||||
selectedMessageType = null;
|
||||
}
|
||||
|
||||
// --- Message Versioning Global Listeners ---
|
||||
|
||||
// Global click listener for buttons and (de)selecting messages
|
||||
document.addEventListener('click', function(e) {
|
||||
const target = e.target;
|
||||
|
||||
const msg = target.closest('.message, .user-message, .assistant-message')
|
||||
if (msg) {
|
||||
const historyIndex = msg.getAttribute('data-history-index');
|
||||
const msgType = msg.getAttribute('data-message-type') ?? (msg.classList.contains('assistant-message') ? 1 : 0);
|
||||
if (target.closest('button')) {
|
||||
const button = target.closest('.message-versioning-nav-arrow');
|
||||
if (button && button.hasAttribute('activated')) {
|
||||
const direction = button.getAttribute('data-direction');
|
||||
versioningNavigateClick(button, parseFloat(historyIndex), parseFloat(msgType), direction);
|
||||
}
|
||||
} else if (msg.classList.contains('selected-message') && !e.ctrlKey) {
|
||||
versioningDeselectMessages();
|
||||
} else {
|
||||
versioningSelectMessage(msg, parseInt(historyIndex), parseInt(msgType));
|
||||
}
|
||||
} else if (target.closest('#chat') && !target.closest('#message-versioning-navigate-hidden')) { // Deselect if the click is in-chat, outside a message
|
||||
versioningDeselectMessages();
|
||||
}
|
||||
});
|
||||
|
||||
// Global keydown listener for keyboard navigation
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use Ctrl + Left/Right Arrow Keys for navigation, Ctrl + Up/Down Arrow Keys for selection
|
||||
if (e.ctrlKey && !e.shiftKey) {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
triggerVersionNavigateBackend(selectedMessageHistoryIndex, selectedMessageType, 'left');
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
triggerVersionNavigateBackend(selectedMessageHistoryIndex, selectedMessageType, 'right');
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
selectRelativeMessage(-1)
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
selectRelativeMessage(1)
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function selectRelativeMessage(offset) {
|
||||
const chat = gradioApp().querySelector('#chat');
|
||||
if (!chat) return;
|
||||
const messages = Array.from(chat.querySelectorAll('.message, .user-message, .assistant-message'));
|
||||
if (messages.length === 0) return;
|
||||
const selectedMessageChatIndex = messages.findIndex(msg => msg.classList.contains('selected-message')); // Could be saved in a variable rather than run each time
|
||||
const index = selectedMessageChatIndex + offset;
|
||||
if (index >= 0 && index < messages.length) messages[index]?.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ from jinja2.sandbox import ImmutableSandboxedEnvironment
|
|||
from PIL import Image
|
||||
|
||||
import modules.shared as shared
|
||||
from modules import utils
|
||||
from modules import utils, message_versioning
|
||||
from modules.extensions import apply_extensions
|
||||
from modules.html_generator import (
|
||||
chat_html_wrapper,
|
||||
|
@ -667,6 +667,14 @@ def generate_chat_reply_wrapper(text, state, regenerate=False, _continue=False):
|
|||
send_dummy_reply(state['start_with'], state)
|
||||
|
||||
history = state['history']
|
||||
initial_history_len = len(history['internal'])
|
||||
if not regenerate and not _continue and text.strip():
|
||||
temp_history = copy.deepcopy(history)
|
||||
visible_text = html.escape(text)
|
||||
temp_history['internal'].append([text, ''])
|
||||
temp_history['visible'].append([visible_text, ''])
|
||||
message_versioning.append_message_version(temp_history, state, is_bot=False)
|
||||
|
||||
last_save_time = time.monotonic()
|
||||
save_interval = 8
|
||||
for i, history in enumerate(generate_chat_reply(text, state, regenerate, _continue, loading_message=True, for_ui=True)):
|
||||
|
@ -677,9 +685,14 @@ def generate_chat_reply_wrapper(text, state, regenerate=False, _continue=False):
|
|||
if i == 0 or (current_time - last_save_time) >= save_interval:
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
last_save_time = current_time
|
||||
|
||||
if len(history['internal']) > initial_history_len or regenerate or _continue:
|
||||
message_versioning.append_message_version(history, state, is_bot=True)
|
||||
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
|
||||
yield chat_html_wrapper(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu']), history
|
||||
|
||||
|
||||
def remove_last_message(history):
|
||||
if 'metadata' not in history:
|
||||
|
@ -822,6 +835,7 @@ def rename_history(old_id, new_id, character, mode):
|
|||
else:
|
||||
logger.info(f"Renaming \"{old_p}\" to \"{new_p}\"")
|
||||
old_p.rename(new_p)
|
||||
message_versioning.rename_history_data(old_id, new_id, character, mode)
|
||||
|
||||
|
||||
def get_paths(state):
|
||||
|
@ -1317,8 +1331,11 @@ def my_yaml_output(data):
|
|||
|
||||
|
||||
def handle_replace_last_reply_click(text, state):
|
||||
last_reply = state['history']['internal'][-1][1] if len(state['history']['internal']) > 0 else None
|
||||
history = replace_last_reply(text, state)
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
if len(history['internal']) > 0 and history['internal'][-1][1] != last_reply: # Differs from last reply
|
||||
message_versioning.append_message_version(history, state, is_bot=True)
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
|
||||
return [history, html, {"text": "", "files": []}]
|
||||
|
@ -1327,6 +1344,7 @@ def handle_replace_last_reply_click(text, state):
|
|||
def handle_send_dummy_message_click(text, state):
|
||||
history = send_dummy_message(text, state)
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
message_versioning.append_message_version(history, state, is_bot=False)
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
|
||||
return [history, html, {"text": "", "files": []}]
|
||||
|
@ -1335,6 +1353,7 @@ def handle_send_dummy_message_click(text, state):
|
|||
def handle_send_dummy_reply_click(text, state):
|
||||
history = send_dummy_reply(text, state)
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
message_versioning.append_message_version(history, state, is_bot=True)
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
|
||||
return [history, html, {"text": "", "files": []}]
|
||||
|
@ -1373,8 +1392,18 @@ def handle_start_new_chat_click(state):
|
|||
|
||||
|
||||
def handle_delete_chat_confirm_click(state):
|
||||
index = str(find_all_histories(state).index(state['unique_id']))
|
||||
delete_history(state['unique_id'], state['character_menu'], state['mode'])
|
||||
unique_id_to_delete = state['unique_id']
|
||||
character_to_delete = state['character_menu']
|
||||
mode_to_delete = state['mode']
|
||||
all_histories = find_all_histories(state)
|
||||
index = '0'
|
||||
if unique_id_to_delete in all_histories:
|
||||
index = str(all_histories.index(unique_id_to_delete))
|
||||
|
||||
delete_history(unique_id_to_delete, character_to_delete, mode_to_delete)
|
||||
message_versioning.clear_history_data(unique_id_to_delete, character_to_delete, mode_to_delete)
|
||||
|
||||
# Load the next appropriate history
|
||||
history, unique_id = load_history_after_deletion(state, index)
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from pathlib import Path
|
|||
import markdown
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from modules import shared
|
||||
from modules import shared, message_versioning
|
||||
from modules.sane_markdown_lists import SaneListExtension
|
||||
from modules.utils import get_available_chat_styles
|
||||
|
||||
|
@ -379,7 +379,6 @@ def format_message_attachments(history, role, index):
|
|||
|
||||
return ""
|
||||
|
||||
|
||||
def actions_html(history, i, info_message=""):
|
||||
return (f'<div class="message-actions">'
|
||||
f'{copy_button}'
|
||||
|
@ -398,6 +397,8 @@ def generate_instruct_html(history):
|
|||
row_visible = history['visible'][i]
|
||||
row_internal = history['internal'][i]
|
||||
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
|
||||
versioning_nav_user = message_versioning.get_message_version_nav_elements(i, 0)
|
||||
versioning_nav_bot = message_versioning.get_message_version_nav_elements(i, 1)
|
||||
|
||||
# Get timestamps
|
||||
user_timestamp = format_message_timestamp(history, "user", i)
|
||||
|
@ -421,8 +422,10 @@ def generate_instruct_html(history):
|
|||
info_message_assistant = info_button.replace("message", assistant_timestamp_value)
|
||||
|
||||
if converted_visible[0]: # Don't display empty user messages
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 0) else ""
|
||||
output += (
|
||||
f'<div class="user-message" '
|
||||
f'<div class="user-message{selected_class}" '
|
||||
f'data-history-index="{i}" '
|
||||
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
||||
f'<div class="text">'
|
||||
f'<div class="message-body">{converted_visible[0]}</div>'
|
||||
|
@ -432,8 +435,9 @@ def generate_instruct_html(history):
|
|||
f'</div>'
|
||||
)
|
||||
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 1) else ""
|
||||
output += (
|
||||
f'<div class="assistant-message" '
|
||||
f'<div class="assistant-message{selected_class}" '
|
||||
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
|
||||
f'data-index={i}>'
|
||||
f'<div class="text">'
|
||||
|
@ -466,6 +470,8 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
|||
row_visible = history['visible'][i]
|
||||
row_internal = history['internal'][i]
|
||||
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
|
||||
versioning_nav_user = message_versioning.get_message_version_nav_elements(i, 0)
|
||||
versioning_nav_bot = message_versioning.get_message_version_nav_elements(i, 1)
|
||||
|
||||
# Get timestamps
|
||||
user_timestamp = format_message_timestamp(history, "user", i)
|
||||
|
@ -476,8 +482,10 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
|||
assistant_attachments = format_message_attachments(history, "assistant", i)
|
||||
|
||||
if converted_visible[0]: # Don't display empty user messages
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 0) else ""
|
||||
output += (
|
||||
f'<div class="message" '
|
||||
f'<div class="message{selected_class}" '
|
||||
f'data-history-index="{i}" data-message-type="0" '
|
||||
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
||||
f'<div class="circle-you">{img_me}</div>'
|
||||
f'<div class="text">'
|
||||
|
@ -489,8 +497,9 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
|||
f'</div>'
|
||||
)
|
||||
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 1) else ""
|
||||
output += (
|
||||
f'<div class="message" '
|
||||
f'<div class="message"{selected_class}'
|
||||
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
|
||||
f'data-index={i}>'
|
||||
f'<div class="circle-bot">{img_bot}</div>'
|
||||
|
@ -514,6 +523,8 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
|||
row_visible = history['visible'][i]
|
||||
row_internal = history['internal'][i]
|
||||
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
|
||||
versioning_nav_user = message_versioning.get_message_version_nav_elements(i, 0)
|
||||
versioning_nav_bot = message_versioning.get_message_version_nav_elements(i, 1)
|
||||
|
||||
# Get timestamps
|
||||
user_timestamp = format_message_timestamp(history, "user", i)
|
||||
|
@ -537,8 +548,10 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
|||
info_message_assistant = info_button.replace("message", assistant_timestamp_value)
|
||||
|
||||
if converted_visible[0]: # Don't display empty user messages
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 0) else ""
|
||||
output += (
|
||||
f'<div class="message" '
|
||||
f'<div class="message{selected_class}" '
|
||||
f'data-history-index="{i}"'
|
||||
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
||||
f'<div class="text-you">'
|
||||
f'<div class="message-body">{converted_visible[0]}</div>'
|
||||
|
@ -548,8 +561,9 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
|||
f'</div>'
|
||||
)
|
||||
|
||||
selected_class = " selected-message" if message_versioning.is_message_selected(i, 1) else ""
|
||||
output += (
|
||||
f'<div class="message" '
|
||||
f'<div class="message{selected_class}" '
|
||||
f'data-raw="{html.escape(row_internal[1], quote=True)}"'
|
||||
f'data-index={i}>'
|
||||
f'<div class="text-bot">'
|
||||
|
|
490
modules/message_versioning.py
Normal file
490
modules/message_versioning.py
Normal file
|
@ -0,0 +1,490 @@
|
|||
from typing import Dict, List, Optional, Iterable
|
||||
from functools import reduce
|
||||
from operator import getitem
|
||||
from modules import shared
|
||||
from pathlib import Path
|
||||
import json
|
||||
import logging as logger
|
||||
|
||||
# --- History Storage Logic ---
|
||||
|
||||
# Global variables for the currently loaded history data and state
|
||||
loaded_history = {'visible': [], 'internal': []}
|
||||
last_state = {'character_menu': None, 'unique_id': None, 'mode': None, 'display_mode': 'html'}
|
||||
|
||||
|
||||
def validate_list(lst: List, i: int):
|
||||
"""Ensure list is properly extended to index i"""
|
||||
if len(lst) <= i:
|
||||
lst.extend([None] * (i + 1 - len(lst)))
|
||||
|
||||
|
||||
def validate_history_structure(history_data: Dict, i: int):
|
||||
"""Ensure history_data structure is properly extended to index i"""
|
||||
for history_type in ['visible', 'internal']:
|
||||
if history_type not in history_data:
|
||||
history_data[history_type] = []
|
||||
validate_list(history_data[history_type], i)
|
||||
|
||||
|
||||
def init_history_entry(history_data: Dict, i: int):
|
||||
"""Initialize history entry dicts at index i if nonexistent"""
|
||||
for history_type in ['visible', 'internal']:
|
||||
if history_data[history_type][i] is None or not isinstance(history_data[history_type][i], list):
|
||||
history_data[history_type][i] = [
|
||||
{'text': [], 'pos': 0}, # User message data
|
||||
{'text': [], 'pos': 0} # Bot message data
|
||||
]
|
||||
|
||||
|
||||
def load_or_init_history_data(character: Optional[str], unique_id: Optional[str], mode: str) -> Dict:
|
||||
"""
|
||||
Loads history data from file if character and unique_id are provided, the file exists,
|
||||
and the requested context (character, unique_id, mode) differs from the currently loaded context.
|
||||
Otherwise, returns the existing in-memory history or a new empty history structure.
|
||||
"""
|
||||
global loaded_history, last_state
|
||||
|
||||
_character = last_state.get('character_menu')
|
||||
_unique_id = last_state.get('unique_id')
|
||||
_mode = last_state.get('mode')
|
||||
|
||||
# --- Check if requested context matches already loaded context ---
|
||||
if (character == _character
|
||||
and unique_id == _unique_id # noqa: W503
|
||||
and mode == _mode # noqa: W503
|
||||
and _character is not None): # noqa: W503
|
||||
return loaded_history
|
||||
|
||||
# --- Context differs or history data not loaded, proceed with load/initialization ---
|
||||
def _create_empty_history():
|
||||
return {'visible': [], 'internal': []}
|
||||
|
||||
if not character or not unique_id:
|
||||
logger.warning("Cannot load history data: Character or ID is missing.")
|
||||
loaded_history = _create_empty_history()
|
||||
return loaded_history
|
||||
|
||||
path = get_history_data_path(unique_id, character, mode)
|
||||
if not path.exists():
|
||||
loaded_history = _create_empty_history()
|
||||
return loaded_history
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
contents = f.read()
|
||||
if contents:
|
||||
loaded_data = json.loads(contents)
|
||||
last_state['character_menu'] = character
|
||||
last_state['unique_id'] = unique_id
|
||||
last_state['mode'] = mode
|
||||
loaded_history = loaded_data
|
||||
return loaded_history
|
||||
else:
|
||||
loaded_history = _create_empty_history()
|
||||
return loaded_history
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"Error decoding JSON from history data file: {path}. Initializing empty.", exc_info=True)
|
||||
loaded_history = _create_empty_history()
|
||||
return loaded_history
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading message versioning history data from {path}: {e}", exc_info=True)
|
||||
loaded_history = _create_empty_history()
|
||||
return loaded_history
|
||||
|
||||
|
||||
def append_message_version(history: Dict, state: Dict, is_bot=True):
|
||||
"""
|
||||
Append a message to the end of the currently loaded history data (loaded_history).
|
||||
Requires state to potentially save the updated history.
|
||||
"""
|
||||
global loaded_history
|
||||
|
||||
# NOTE: Assumes the correct history for the state's character/unique_id is already loaded into loaded_history
|
||||
|
||||
msg_type = 1 if is_bot else 0
|
||||
if not history or not history.get('visible') or not history.get('internal'):
|
||||
logger.warning("Attempted to append to history data with invalid history.")
|
||||
return
|
||||
|
||||
i = len(history['visible']) - 1
|
||||
if i < 0 or len(history['visible'][i]) <= msg_type or len(history['internal'][i]) <= msg_type:
|
||||
logger.warning(f"Input history index {i} or msg_type {msg_type} out of bounds for appending.")
|
||||
return
|
||||
|
||||
visible_text = history['visible'][i][msg_type]
|
||||
if not visible_text:
|
||||
return
|
||||
internal_text = history['internal'][i][msg_type]
|
||||
|
||||
try:
|
||||
validate_history_structure(loaded_history, i)
|
||||
init_history_entry(loaded_history, i)
|
||||
|
||||
# Append the strings to the respective lists in loaded_history
|
||||
if 'text' not in loaded_history['visible'][i][msg_type]:
|
||||
loaded_history['visible'][i][msg_type]['text'] = []
|
||||
if 'text' not in loaded_history['internal'][i][msg_type]:
|
||||
loaded_history['internal'][i][msg_type]['text'] = []
|
||||
|
||||
vis_list = loaded_history['visible'][i][msg_type]['text']
|
||||
int_list = loaded_history['internal'][i][msg_type]['text']
|
||||
|
||||
vis_list.append(visible_text)
|
||||
int_list.append(internal_text)
|
||||
|
||||
# Update position to the new last index
|
||||
new_pos = len(vis_list) - 1
|
||||
loaded_history['visible'][i][msg_type]['pos'] = new_pos
|
||||
loaded_history['internal'][i][msg_type]['pos'] = new_pos
|
||||
|
||||
character = state['character_menu']
|
||||
unique_id = state['unique_id']
|
||||
mode = state['mode']
|
||||
if character and unique_id:
|
||||
save_history_data(character, unique_id, mode, loaded_history)
|
||||
else:
|
||||
logger.warning("Could not save history data after append: character or unique_id missing in state.")
|
||||
except IndexError:
|
||||
logger.error(f"IndexError adding message version: index={i}, msg_type={msg_type}. Message versions: {loaded_history}", exc_info=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding message version: {e}", exc_info=True)
|
||||
return
|
||||
|
||||
|
||||
def save_history_data(character: str, unique_id: str, mode: str, history_data: Dict) -> bool:
|
||||
"""Save the provided history data structure to disk"""
|
||||
|
||||
if not unique_id or not character:
|
||||
logger.warning("Cannot save history data: Character or ID is not set.")
|
||||
return False
|
||||
|
||||
current_mode = mode or last_state.get('mode') or shared.persistent_interface_state.get('mode', 'chat')
|
||||
|
||||
path = get_history_data_path(unique_id, character, current_mode)
|
||||
try:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
data_to_save = {'visible': history_data['visible'], 'internal': history_data['internal']}
|
||||
json.dump(data_to_save, f, indent=4)
|
||||
return True
|
||||
except TypeError as e:
|
||||
logger.error(f"TypeError saving history data (possibly non-serializable data): {e}. Data: {history_data}", exc_info=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving message versioning history data to {path}: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
|
||||
def get_history_data_path(unique_id: str, character: str, mode: str) -> Path:
|
||||
"""Get the path to the history data file"""
|
||||
if not unique_id or not character or not mode:
|
||||
raise ValueError("unique_id, character, and mode must be provided to get history data path.")
|
||||
|
||||
try:
|
||||
import modules.chat as chat
|
||||
base_history_path = chat.get_history_file_path(unique_id, character, mode)
|
||||
path = base_history_path.parent
|
||||
except Exception as e:
|
||||
# Fallback or error handling if get_history_file_path fails (shouldn't happen, but just in case)
|
||||
logger.error(f"Failed to get base history path via get_history_file_path: {e}.", exc_info=True)
|
||||
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
storage_filename = f'{unique_id}.boogaplus'
|
||||
final_path = path / storage_filename
|
||||
return final_path
|
||||
|
||||
|
||||
# --- Functions to be called by integrated logic ---
|
||||
|
||||
|
||||
def set_versioning_display_mode(new_dmode: str):
|
||||
"""Sets the display mode ('html', 'overlay', 'off')."""
|
||||
global last_state
|
||||
last_state['display_mode'] = new_dmode
|
||||
|
||||
|
||||
def get_versioning_display_mode():
|
||||
"""Gets the current display mode from the loaded history."""
|
||||
current_mode = last_state.get('display_mode', 'html')
|
||||
return current_mode
|
||||
|
||||
|
||||
def clear_history_data(unique_id: str, character: str, mode: str):
|
||||
"""
|
||||
Deletes the history data file associated with a history.
|
||||
NOTE: This function does NOT clear the in-memory loaded_history.
|
||||
The caller should handle resetting/reloading the in-memory data if the deleted history was the active one.
|
||||
"""
|
||||
try:
|
||||
history_data_path = get_history_data_path(unique_id, character, mode)
|
||||
if history_data_path.exists():
|
||||
history_data_path.unlink()
|
||||
else:
|
||||
logger.warning(f"Message versioning history data file not found for deletion: {history_data_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting message versioning history data file {history_data_path}: {e}", exc_info=True)
|
||||
|
||||
|
||||
def rename_history_data(old_id: str, new_id: str, character: str, mode: str):
|
||||
"""
|
||||
Renames the history data file when a history is renamed.
|
||||
NOTE: This function does NOT update the in-memory loaded_history if the renamed history was the active one.
|
||||
The caller should handle reloading the data under the new ID if necessary.
|
||||
"""
|
||||
try:
|
||||
old_history_data_path = get_history_data_path(old_id, character, mode)
|
||||
new_history_data_path = get_history_data_path(new_id, character, mode)
|
||||
|
||||
if new_history_data_path.exists():
|
||||
logger.warning(f"Cannot rename message versioning history data: Target path {new_history_data_path} already exists.")
|
||||
return
|
||||
|
||||
if old_history_data_path.exists():
|
||||
old_history_data_path.rename(new_history_data_path)
|
||||
else:
|
||||
logger.warning(f"Message versioning history data file not found for renaming: {old_history_data_path}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error renaming message versioning history data file from {old_id} to {new_id}: {e}", exc_info=True)
|
||||
|
||||
|
||||
# --- Core Logic ---
|
||||
|
||||
last_history_index = -1
|
||||
last_msg_type = -1
|
||||
|
||||
|
||||
# --- Helper Functions ---
|
||||
|
||||
def recursive_get(data: Dict | Iterable, keyList: List[int | str], default=None):
|
||||
"""Iterate nested dictionary / iterable safely."""
|
||||
try:
|
||||
result = reduce(getitem, keyList, data)
|
||||
return result
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return default
|
||||
except Exception as e:
|
||||
logger.error(f"Error in recursive_get: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
def length(data: Iterable):
|
||||
"""Get length of iterable with try-catch."""
|
||||
try:
|
||||
result = len(data)
|
||||
return result
|
||||
except TypeError:
|
||||
return 0
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting length: {e}", exc_info=True)
|
||||
return 0
|
||||
|
||||
|
||||
# --- Core Functions ---
|
||||
|
||||
def get_message_positions(history_index: int, msg_type: int):
|
||||
"""
|
||||
Get the message position and total message versions for a specific message from the currently loaded history data.
|
||||
Assumes the correct history data has already been loaded by the calling context.
|
||||
"""
|
||||
global loaded_history
|
||||
|
||||
# Path: ['visible' or 'internal'][history_index][msg_type]['pos' or 'text']
|
||||
msg_data = recursive_get(loaded_history, ['visible', history_index, msg_type])
|
||||
|
||||
if msg_data is None:
|
||||
logger.warning(f"History data not found for index {history_index}, type {msg_type}. Was history loaded correctly before calling this?")
|
||||
return 0, 0
|
||||
|
||||
pos = recursive_get(msg_data, ['pos'], 0)
|
||||
total = length(recursive_get(msg_data, ['text'], []))
|
||||
return pos, total
|
||||
|
||||
|
||||
def navigate_message_version(history_index: float, msg_type: float, direction: str, history: Dict, character: str, unique_id: str, mode: str):
|
||||
"""
|
||||
Handles navigation (left/right swipe) for a message version.
|
||||
Updates the history dictionary directly and returns the modified history.
|
||||
The calling function will be responsible for regenerating the HTML.
|
||||
"""
|
||||
global loaded_history, last_history_index, last_msg_type
|
||||
try:
|
||||
i = int(history_index)
|
||||
m_type = int(msg_type)
|
||||
last_history_index = i # Default to current index/type
|
||||
last_msg_type = m_type
|
||||
|
||||
load_or_init_history_data(character, unique_id, mode)
|
||||
current_pos, total_pos = get_message_positions(i, m_type)
|
||||
|
||||
if total_pos <= 1:
|
||||
return history
|
||||
|
||||
# Calculate new position
|
||||
new_pos = current_pos
|
||||
if direction == "right":
|
||||
new_pos += 1
|
||||
if new_pos >= total_pos:
|
||||
return history
|
||||
elif direction == "left":
|
||||
new_pos -= 1
|
||||
if new_pos < 0:
|
||||
return history
|
||||
else:
|
||||
logger.warning(f"Invalid navigation direction: {direction}")
|
||||
return history
|
||||
|
||||
loaded_history_data = loaded_history
|
||||
|
||||
visible_msg_data = recursive_get(loaded_history_data, ['visible', i, m_type])
|
||||
internal_msg_data = recursive_get(loaded_history_data, ['internal', i, m_type])
|
||||
|
||||
if not visible_msg_data or not internal_msg_data or 'text' not in visible_msg_data or 'text' not in internal_msg_data:
|
||||
logger.error(f"Loaded history data structure invalid during navigation for index {i}, type {m_type}. Data: {loaded_history_data}")
|
||||
return history
|
||||
|
||||
# Check bounds for the new position against the text lists
|
||||
if new_pos < 0 or new_pos >= len(visible_msg_data['text']):
|
||||
logger.error(f"Navigation error: new_pos {new_pos} out of bounds for visible history text list (len {len(visible_msg_data['text'])})")
|
||||
return history
|
||||
if new_pos < 0 or new_pos >= len(internal_msg_data['text']):
|
||||
logger.error(f"Navigation error: new_pos {new_pos} out of bounds for internal history text list (len {len(internal_msg_data['text'])})")
|
||||
return history
|
||||
|
||||
new_visible = visible_msg_data['text'][new_pos]
|
||||
new_internal = internal_msg_data['text'][new_pos]
|
||||
|
||||
# Update the position ('pos') within the loaded history data structure
|
||||
visible_msg_data['pos'] = new_pos
|
||||
internal_msg_data['pos'] = new_pos
|
||||
|
||||
if character and unique_id:
|
||||
save_history_data(character, unique_id, mode, loaded_history_data)
|
||||
else:
|
||||
logger.warning("Could not save history data after navigation: character or unique_id missing in state.")
|
||||
|
||||
# Update the main chat history dictionary in the state
|
||||
current_history = history
|
||||
if i < len(current_history['visible']) and m_type < len(current_history['visible'][i]):
|
||||
current_history['visible'][i][m_type] = new_visible
|
||||
else:
|
||||
logger.error(f"History structure invalid (visible) for update at index {i}, type {m_type}")
|
||||
|
||||
if i < len(current_history['internal']) and m_type < len(current_history['internal'][i]):
|
||||
current_history['internal'][i][m_type] = new_internal
|
||||
else:
|
||||
logger.error(f"History structure invalid (internal) for update at index {i}, type {m_type}")
|
||||
|
||||
last_history_index = i
|
||||
last_msg_type = m_type
|
||||
|
||||
return current_history
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during message version navigation: {e}", exc_info=True)
|
||||
return history
|
||||
|
||||
|
||||
# --- Functions to be called during HTML Generation ---
|
||||
|
||||
|
||||
def get_message_version_nav_elements(history_index: int, msg_type: int):
|
||||
"""
|
||||
Generates the HTML for the message version navigation arrows and position indicator.
|
||||
Should be called by the HTML generator.
|
||||
Returns an empty string if history storage mode is 'off' or navigation is not needed.
|
||||
"""
|
||||
if get_versioning_display_mode() == 'off':
|
||||
return ""
|
||||
|
||||
try:
|
||||
current_pos, total_pos = get_message_positions(history_index, msg_type)
|
||||
|
||||
if total_pos <= 1:
|
||||
return ""
|
||||
|
||||
left_activated_attr = f' activated="true" data-history-index="{history_index}" data-msg-type="{msg_type}" data-direction="left"' if current_pos > 0 else ''
|
||||
right_activated_attr = f' activated="true" data-history-index="{history_index}" data-msg-type="{msg_type}" data-direction="right"' if current_pos < total_pos - 1 else ''
|
||||
|
||||
# Similar size to copy/refresh icons
|
||||
left_arrow_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>'''
|
||||
right_arrow_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>'''
|
||||
|
||||
nav_left = f'<button class="message-versioning-nav-arrow message-versioning-nav-left footer-button"{left_activated_attr}>{left_arrow_svg}</button>'
|
||||
nav_pos = f'<div class="message-versioning-nav-pos">{current_pos + 1}/{total_pos}</div>'
|
||||
nav_right = f'<button class="message-versioning-nav-arrow message-versioning-nav-right footer-button"{right_activated_attr}>{right_arrow_svg}</button>'
|
||||
|
||||
nav_container = (
|
||||
f'<div class="message-versioning-nav-container">'
|
||||
f'{nav_left}'
|
||||
f'{nav_pos}'
|
||||
f'{nav_right}'
|
||||
f'</div>'
|
||||
)
|
||||
|
||||
versioning_container = (
|
||||
f'<div class="message-versioning-container">'
|
||||
f'{nav_container}'
|
||||
f'</div>'
|
||||
)
|
||||
return versioning_container
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating message versioning nav elements for index {history_index}, type {msg_type}: {e}", exc_info=True)
|
||||
return ""
|
||||
|
||||
|
||||
def is_message_selected(history_index: int, msg_type: int) -> bool:
|
||||
"""
|
||||
Returns True if the message at the specified index and type is selected.
|
||||
"""
|
||||
global last_history_index, last_msg_type
|
||||
return last_history_index == history_index and last_msg_type == msg_type
|
||||
|
||||
|
||||
# --- Functions to be called by Gradio Event Handlers ---
|
||||
|
||||
|
||||
def handle_unique_id_change(history: Dict, unique_id: str, name1: str, name2: str, mode: str, chat_style: str, character: str):
|
||||
"""
|
||||
Handles changes to the unique_id.
|
||||
Loads the corresponding message versioning history data.
|
||||
"""
|
||||
global last_history_index, last_msg_type
|
||||
last_history_index = -1
|
||||
last_msg_type = -1
|
||||
|
||||
if not unique_id:
|
||||
logger.warning("handle_unique_id_change called with empty unique_id.")
|
||||
return
|
||||
|
||||
import modules.chat as chat
|
||||
if not character:
|
||||
logger.warning(f"Cannot load history for unique_id {unique_id}: character_menu not found in state.")
|
||||
# Avoid using stale data from a previous selection
|
||||
load_or_init_history_data(None, unique_id, mode)
|
||||
else:
|
||||
load_or_init_history_data(character, unique_id, mode)
|
||||
|
||||
return chat.redraw_html(history, name1, name2, mode, chat_style, character)
|
||||
|
||||
|
||||
def handle_display_mode_change(display_mode: str):
|
||||
"""
|
||||
Handles changes to the message versioning display mode radio button.
|
||||
Updates the history storage mode. The UI redraw should be triggered by the main logic.
|
||||
"""
|
||||
set_versioning_display_mode(display_mode)
|
||||
|
||||
|
||||
def handle_navigate_click(history_index: float, msg_type: float, direction: str, history: Dict, character: str, unique_id: str, mode: str, name1: str, name2: str, chat_style: str):
|
||||
"""
|
||||
Handles clicks on the navigation buttons generated by get_message_version_nav_elements.
|
||||
Calls navigate_message_version and returns the updated history and potentially the regenerated HTML.
|
||||
"""
|
||||
updated_history = navigate_message_version(history_index, msg_type, direction, history, character, unique_id, mode)
|
||||
|
||||
import modules.chat as chat
|
||||
new_html = chat.redraw_html(updated_history, name1, name2, mode, chat_style, character)
|
||||
|
||||
return updated_history, new_html
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||
import gradio as gr
|
||||
from PIL import Image
|
||||
|
||||
from modules import chat, shared, ui, utils
|
||||
from modules import chat, shared, ui, utils, message_versioning
|
||||
from modules.html_generator import chat_html_wrapper
|
||||
from modules.text_generation import stop_everything_event
|
||||
from modules.utils import gradio
|
||||
|
@ -94,9 +94,19 @@ def create_ui():
|
|||
with gr.Row():
|
||||
shared.gradio['chat_style'] = gr.Dropdown(choices=utils.get_available_chat_styles(), label='Chat style', value=shared.settings['chat_style'], visible=shared.settings['mode'] != 'instruct')
|
||||
|
||||
with gr.Row(visible=True): # TODO: Make visible based on mode
|
||||
shared.gradio['message_versioning_display_mode'] = gr.Radio(choices=['html', 'off'], value='html', label="Message Versioning Display", info="Controls how message version navigation is displayed.", elem_id="message-versioning-display-mode", elem_classes=['slim-dropdown'])
|
||||
|
||||
with gr.Row():
|
||||
shared.gradio['chat-instruct_command'] = gr.Textbox(value=shared.settings['chat-instruct_command'], lines=12, label='Command for chat-instruct mode', info='<|character|> and <|prompt|> get replaced with the bot name and the regular chat prompt respectively.', visible=shared.settings['mode'] == 'chat-instruct', elem_classes=['add_scrollbar'])
|
||||
|
||||
# Hidden elements for message versioning JS communication
|
||||
with gr.Row(visible=False, elem_id="message-versioning-hidden-row"):
|
||||
shared.gradio['message_versioning_history_index_hidden'] = gr.Number(value=0, elem_id="message-versioning-history-index-hidden")
|
||||
shared.gradio['message_versioning_message_type_hidden'] = gr.Number(value=0, elem_id="message-versioning-message-type-hidden")
|
||||
shared.gradio['message_versioning_direction_hidden'] = gr.Textbox(value="", elem_id="message-versioning-direction-hidden")
|
||||
shared.gradio['message_versioning_navigate_hidden'] = gr.Button(elem_id="message-versioning-navigate-hidden")
|
||||
|
||||
|
||||
def create_chat_settings_ui():
|
||||
mu = shared.args.multi_user
|
||||
|
@ -246,6 +256,9 @@ def create_event_handlers():
|
|||
shared.gradio['unique_id'].select(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
chat.handle_unique_id_select, gradio('interface_state'), gradio('history', 'display'), show_progress=False)
|
||||
shared.gradio['unique_id'].change(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
message_versioning.handle_unique_id_change, gradio('history', 'unique_id', 'name1', 'name2', 'mode', 'chat_style', 'character_menu'), gradio('display'), show_progress=False)
|
||||
|
||||
shared.gradio['Start new chat'].click(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
|
@ -293,6 +306,19 @@ def create_event_handlers():
|
|||
shared.gradio['chat_style'].change(chat.redraw_html, gradio(reload_arr), gradio('display'), show_progress=False)
|
||||
shared.gradio['Copy last reply'].click(chat.send_last_reply_to_input, gradio('history'), gradio('textbox'), show_progress=False)
|
||||
|
||||
# Message Versioning event handlers
|
||||
shared.gradio['message_versioning_display_mode'].change(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
message_versioning.handle_display_mode_change, gradio('message_versioning_display_mode'), None).then(
|
||||
chat.redraw_html, gradio(reload_arr), gradio('display'), show_progress=False)
|
||||
|
||||
shared.gradio['message_versioning_navigate_hidden'].click(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
message_versioning.handle_navigate_click,
|
||||
gradio('message_versioning_history_index_hidden', 'message_versioning_message_type_hidden', 'message_versioning_direction_hidden', 'history', 'character_menu', 'unique_id', 'mode', 'name1', 'name2', 'chat_style'),
|
||||
gradio('history', 'display'),
|
||||
show_progress=False)
|
||||
|
||||
# Save/delete a character
|
||||
shared.gradio['save_character'].click(chat.handle_save_character_click, gradio('name2'), gradio('save_character_filename', 'character_saver'), show_progress=False)
|
||||
shared.gradio['delete_character'].click(lambda: gr.update(visible=True), None, gradio('character_deleter'), show_progress=False)
|
||||
|
|
Loading…
Add table
Reference in a new issue