mirror of
https://github.com/oobabooga/text-generation-webui.git
synced 2025-06-07 06:06:20 -04:00
UI: Make message editing work the same for user and assistant messages
This commit is contained in:
parent
6c3590ba9a
commit
27641ac182
5 changed files with 94 additions and 77 deletions
|
@ -186,31 +186,33 @@ function navigateVersion(element, direction) {
|
|||
const index = messageElement.getAttribute("data-index");
|
||||
if (!index) return;
|
||||
|
||||
const indexInput = document.getElementById("Navigate-message-index").querySelector("input");
|
||||
if (!indexInput) {
|
||||
console.error("Element with ID 'Navigate-message-index' not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const directionInput = document.getElementById("Navigate-direction").querySelector("textarea");
|
||||
if (!directionInput) {
|
||||
console.error("Element with ID 'Navigate-direction' not found.");
|
||||
return;
|
||||
// Determine role based on message element classes
|
||||
let role = "assistant"; // Default role
|
||||
if (messageElement.classList.contains("user-message") ||
|
||||
messageElement.querySelector(".text-you") ||
|
||||
messageElement.querySelector(".circle-you")) {
|
||||
role = "user";
|
||||
}
|
||||
|
||||
const indexInput = document.getElementById("Navigate-message-index")?.querySelector("input");
|
||||
const directionInput = document.getElementById("Navigate-direction")?.querySelector("textarea");
|
||||
const roleInput = document.getElementById("Navigate-message-role")?.querySelector("textarea");
|
||||
const navigateButton = document.getElementById("Navigate-version");
|
||||
if (!navigateButton) {
|
||||
console.error("Required element 'Navigate-version' not found.");
|
||||
|
||||
if (!indexInput || !directionInput || !roleInput || !navigateButton) {
|
||||
console.error("Navigation control elements (index, direction, role, or button) not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
indexInput.value = index;
|
||||
directionInput.value = direction;
|
||||
roleInput.value = role;
|
||||
|
||||
// Trigger any 'change' or 'input' events Gradio might be listening for
|
||||
// Trigger 'input' events for Gradio to pick up changes
|
||||
const event = new Event("input", { bubbles: true });
|
||||
indexInput.dispatchEvent(event);
|
||||
directionInput.dispatchEvent(event);
|
||||
roleInput.dispatchEvent(event);
|
||||
|
||||
navigateButton.click();
|
||||
}
|
||||
|
|
|
@ -451,19 +451,21 @@ def get_stopping_strings(state):
|
|||
return result
|
||||
|
||||
|
||||
def add_message_version(history, row_idx, is_current=True):
|
||||
key = f"assistant_{row_idx}"
|
||||
def add_message_version(history, role, row_idx, is_current=True):
|
||||
key = f"{role}_{row_idx}"
|
||||
if 'metadata' not in history:
|
||||
history['metadata'] = {}
|
||||
if key not in history['metadata']:
|
||||
history['metadata'][key] = {}
|
||||
|
||||
if "versions" not in history['metadata'][key]:
|
||||
history['metadata'][key]["versions"] = []
|
||||
|
||||
current_content = history['internal'][row_idx][1]
|
||||
current_visible = history['visible'][row_idx][1]
|
||||
# Determine which index to use for content based on role
|
||||
content_idx = 0 if role == 'user' else 1
|
||||
current_content = history['internal'][row_idx][content_idx]
|
||||
current_visible = history['visible'][row_idx][content_idx]
|
||||
|
||||
# Always add the current message as a new version entry.
|
||||
# The timestamp will differentiate it even if content is identical to a previous version.
|
||||
history['metadata'][key]["versions"].append({
|
||||
"content": current_content,
|
||||
"visible_content": current_visible,
|
||||
|
@ -594,7 +596,7 @@ def chatbot_wrapper(text, state, regenerate=False, _continue=False, loading_mess
|
|||
|
||||
# Store the first response as a version before regenerating
|
||||
if not output['metadata'].get(f"assistant_{row_idx}", {}).get('versions'):
|
||||
add_message_version(output, row_idx, is_current=False)
|
||||
add_message_version(output, "assistant", row_idx, is_current=False)
|
||||
|
||||
if loading_message:
|
||||
yield {
|
||||
|
@ -656,12 +658,13 @@ def chatbot_wrapper(text, state, regenerate=False, _continue=False, loading_mess
|
|||
if is_stream:
|
||||
yield output
|
||||
|
||||
output['visible'][-1][1] = apply_extensions('output', output['visible'][-1][1], state, is_chat=True)
|
||||
|
||||
# Add the newly generated response as a version (only for regeneration)
|
||||
if regenerate:
|
||||
row_idx = len(output['internal']) - 1
|
||||
add_message_version(output, row_idx, is_current=True)
|
||||
add_message_version(output, "assistant", row_idx, is_current=True)
|
||||
|
||||
output['visible'][-1][1] = apply_extensions('output', output['visible'][-1][1], state, is_chat=True)
|
||||
yield output
|
||||
|
||||
|
||||
|
@ -1441,37 +1444,35 @@ def handle_edit_message_click(state):
|
|||
|
||||
if message_index >= len(history['internal']):
|
||||
html_output = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
return [history, html_output, gr.update()]
|
||||
return [history, html_output, gr.update()] # No unique_id change
|
||||
|
||||
# Use the role passed from frontend
|
||||
is_user_msg = (role == "user")
|
||||
role_idx = 0 if is_user_msg else 1
|
||||
role_idx = 0 if role == "user" else 1
|
||||
|
||||
# For assistant messages, save the original version BEFORE updating content
|
||||
if not is_user_msg:
|
||||
if not history['metadata'].get(f"assistant_{message_index}", {}).get('versions'):
|
||||
add_message_version(history, message_index, is_current=False)
|
||||
if 'metadata' not in history:
|
||||
history['metadata'] = {}
|
||||
|
||||
key = f"{role}_{message_index}"
|
||||
if key not in history['metadata']:
|
||||
history['metadata'][key] = {}
|
||||
|
||||
# If no versions exist yet for this message, store the current (pre-edit) content as the first version.
|
||||
if "versions" not in history['metadata'][key] or not history['metadata'][key]["versions"]:
|
||||
original_content = history['internal'][message_index][role_idx]
|
||||
original_visible = history['visible'][message_index][role_idx]
|
||||
|
||||
history['metadata'][key]["versions"] = [{
|
||||
"content": original_content,
|
||||
"visible_content": original_visible,
|
||||
"timestamp": get_current_timestamp()
|
||||
}]
|
||||
|
||||
# NOW update the message content
|
||||
history['internal'][message_index][role_idx] = apply_extensions('input', new_text, state, is_chat=True)
|
||||
history['visible'][message_index][role_idx] = html.escape(new_text)
|
||||
|
||||
# Branch if editing user message, add version if editing assistant message
|
||||
if is_user_msg:
|
||||
# Branch like branch-here
|
||||
history['visible'] = history['visible'][:message_index + 1]
|
||||
history['internal'] = history['internal'][:message_index + 1]
|
||||
new_unique_id = datetime.now().strftime('%Y%m%d-%H-%M-%S')
|
||||
save_history(history, new_unique_id, state['character_menu'], state['mode'])
|
||||
histories = find_all_histories_with_first_prompts(state)
|
||||
past_chats_update = gr.update(choices=histories, value=new_unique_id)
|
||||
state['unique_id'] = new_unique_id
|
||||
elif not is_user_msg:
|
||||
# Add the new version as current
|
||||
add_message_version(history, message_index, is_current=True)
|
||||
past_chats_update = gr.update()
|
||||
else:
|
||||
past_chats_update = gr.update()
|
||||
add_message_version(history, role, message_index, is_current=True)
|
||||
|
||||
# Since we are not branching, unique_id does not change.
|
||||
past_chats_update = gr.update()
|
||||
|
||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||
html_output = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
|
@ -1483,33 +1484,36 @@ def handle_navigate_version_click(state):
|
|||
history = state['history']
|
||||
message_index = int(state['navigate_message_index'])
|
||||
direction = state['navigate_direction']
|
||||
role = state['navigate_message_role']
|
||||
|
||||
# Get assistant message metadata
|
||||
key = f"assistant_{message_index}"
|
||||
if key not in history['metadata'] or 'versions' not in history['metadata'][key]:
|
||||
# No versions to navigate
|
||||
if not role:
|
||||
logger.error("Role not provided for version navigation.")
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
return [history, html]
|
||||
|
||||
key = f"{role}_{message_index}"
|
||||
if 'metadata' not in history or key not in history['metadata'] or 'versions' not in history['metadata'][key]:
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
return [history, html]
|
||||
|
||||
metadata = history['metadata'][key]
|
||||
current_idx = metadata.get('current_version_index', 0)
|
||||
versions = metadata['versions']
|
||||
# Default to the last version if current_version_index is not set
|
||||
current_idx = metadata.get('current_version_index', len(versions) - 1 if versions else 0)
|
||||
|
||||
# Calculate new index
|
||||
if direction == 'left':
|
||||
new_idx = max(0, current_idx - 1)
|
||||
else: # right
|
||||
new_idx = min(len(versions) - 1, current_idx + 1)
|
||||
|
||||
if new_idx == current_idx:
|
||||
# No change needed
|
||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||
return [history, html]
|
||||
|
||||
# Update history with new version
|
||||
version = versions[new_idx]
|
||||
history['internal'][message_index][1] = version['content']
|
||||
history['visible'][message_index][1] = version['visible_content']
|
||||
msg_content_idx = 0 if role == 'user' else 1 # 0 for user content, 1 for assistant content in the pair
|
||||
version_to_load = versions[new_idx]
|
||||
history['internal'][message_index][msg_content_idx] = version_to_load['content']
|
||||
history['visible'][message_index][msg_content_idx] = version_to_load['visible_content']
|
||||
metadata['current_version_index'] = new_idx
|
||||
|
||||
# Redraw and save
|
||||
|
|
|
@ -388,16 +388,17 @@ def format_message_attachments(history, role, index):
|
|||
return ""
|
||||
|
||||
|
||||
def get_version_navigation_html(history, i):
|
||||
def get_version_navigation_html(history, i, role):
|
||||
"""Generate simple navigation arrows for message versions"""
|
||||
key = f"assistant_{i}"
|
||||
key = f"{role}_{i}"
|
||||
metadata = history.get('metadata', {})
|
||||
|
||||
if key not in metadata or 'versions' not in metadata[key]:
|
||||
return ""
|
||||
|
||||
versions = metadata[key]['versions']
|
||||
current_idx = metadata[key].get('current_version_index', 0)
|
||||
# Default to the last version if current_version_index isn't set in metadata
|
||||
current_idx = metadata[key].get('current_version_index', len(versions) - 1 if versions else 0)
|
||||
|
||||
if len(versions) <= 1:
|
||||
return ""
|
||||
|
@ -413,22 +414,33 @@ def get_version_navigation_html(history, i):
|
|||
|
||||
|
||||
def actions_html(history, i, role, info_message=""):
|
||||
action_buttons = ""
|
||||
version_nav_html = ""
|
||||
|
||||
if role == "assistant":
|
||||
return (f'<div class="message-actions">'
|
||||
f'{copy_button}'
|
||||
f'{edit_button}'
|
||||
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{branch_button}'
|
||||
f'{info_message}'
|
||||
f'</div>'
|
||||
f'{get_version_navigation_html(history, i)}')
|
||||
return (f'<div class="message-actions">'
|
||||
action_buttons = (
|
||||
f'{copy_button}'
|
||||
f'{edit_button}'
|
||||
f'{refresh_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{continue_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{remove_button if i == len(history["visible"]) - 1 else ""}'
|
||||
f'{branch_button}'
|
||||
)
|
||||
|
||||
version_nav_html = get_version_navigation_html(history, i, "assistant")
|
||||
elif role == "user":
|
||||
action_buttons = (
|
||||
f'{copy_button}'
|
||||
f'{edit_button}'
|
||||
)
|
||||
|
||||
version_nav_html = get_version_navigation_html(history, i, "user")
|
||||
|
||||
return (f'<div class="message-actions">'
|
||||
f'{action_buttons}'
|
||||
f'{info_message}'
|
||||
f'</div>')
|
||||
f'</div>'
|
||||
f'{version_nav_html}')
|
||||
|
||||
|
||||
def generate_instruct_html(history):
|
||||
|
|
|
@ -212,14 +212,13 @@ def list_interface_input_elements():
|
|||
'grammar_string',
|
||||
'navigate_message_index',
|
||||
'navigate_direction',
|
||||
'navigate_message_role',
|
||||
'edit_message_index',
|
||||
'edit_message_text',
|
||||
'edit_message_role',
|
||||
'branch_index',
|
||||
'enable_web_search',
|
||||
'web_search_pages',
|
||||
'navigate_message_index',
|
||||
'navigate_direction',
|
||||
]
|
||||
|
||||
# Chat elements
|
||||
|
|
|
@ -110,6 +110,7 @@ def create_ui():
|
|||
with gr.Row(visible=False):
|
||||
shared.gradio['navigate_message_index'] = gr.Number(value=-1, precision=0, elem_id="Navigate-message-index")
|
||||
shared.gradio['navigate_direction'] = gr.Textbox(value="", elem_id="Navigate-direction")
|
||||
shared.gradio['navigate_message_role'] = gr.Textbox(value="", elem_id="Navigate-message-role")
|
||||
shared.gradio['navigate_version'] = gr.Button(elem_id="Navigate-version")
|
||||
shared.gradio['edit_message_index'] = gr.Number(value=-1, precision=0, elem_id="Edit-message-index")
|
||||
shared.gradio['edit_message_text'] = gr.Textbox(value="", elem_id="Edit-message-text")
|
||||
|
@ -313,8 +314,7 @@ def create_event_handlers():
|
|||
|
||||
shared.gradio['edit_message'].click(
|
||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||
chat.handle_edit_message_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), show_progress=False).then(
|
||||
lambda: None, None, None, js='() => { const role = document.getElementById("Edit-message-role").querySelector("textarea").value; if (role === "user") document.getElementById("Regenerate").click(); }')
|
||||
chat.handle_edit_message_click, gradio('interface_state'), gradio('history', 'display', 'unique_id'), 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)
|
||||
|
|
Loading…
Add table
Reference in a new issue