From e59cfab25326c034b9370c56e00886aa5aadc8b7 Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 00:49:17 -0700
Subject: [PATCH 1/8] Reapply "Dynamic Chat Message UI Update Speed (#6952)"
(for now)
This reverts commit 126b3a768fa9af7f5318dbfd70b7e6ad00defc68.
---
modules/shared.py | 1 -
modules/text_generation.py | 18 ++++++++----------
modules/ui.py | 1 -
modules/ui_parameters.py | 2 --
user_data/settings-template.yaml | 1 -
5 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/modules/shared.py b/modules/shared.py
index d2305f30..9a181f3e 100644
--- a/modules/shared.py
+++ b/modules/shared.py
@@ -47,7 +47,6 @@ settings = {
'max_new_tokens_max': 4096,
'prompt_lookup_num_tokens': 0,
'max_tokens_second': 0,
- 'max_updates_second': 12,
'auto_max_new_tokens': True,
'ban_eos_token': False,
'add_bos_token': True,
diff --git a/modules/text_generation.py b/modules/text_generation.py
index 1fd6d810..0d499d50 100644
--- a/modules/text_generation.py
+++ b/modules/text_generation.py
@@ -65,41 +65,39 @@ def _generate_reply(question, state, stopping_strings=None, is_chat=False, escap
all_stop_strings += st
shared.stop_everything = False
- last_update = -1
reply = ''
is_stream = state['stream']
if len(all_stop_strings) > 0 and not state['stream']:
state = copy.deepcopy(state)
state['stream'] = True
- min_update_interval = 0
- if state.get('max_updates_second', 0) > 0:
- min_update_interval = 1 / state['max_updates_second']
-
# Generate
+ last_update = -1
+ latency_threshold = 1 / 1000
for reply in generate_func(question, original_question, state, stopping_strings, is_chat=is_chat):
+ cur_time = time.monotonic()
reply, stop_found = apply_stopping_strings(reply, all_stop_strings)
if escape_html:
reply = html.escape(reply)
if is_stream:
- cur_time = time.time()
-
# Limit number of tokens/second to make text readable in real time
if state['max_tokens_second'] > 0:
diff = 1 / state['max_tokens_second'] - (cur_time - last_update)
if diff > 0:
time.sleep(diff)
- last_update = time.time()
+ last_update = time.monotonic()
yield reply
# Limit updates to avoid lag in the Gradio UI
# API updates are not limited
else:
- if cur_time - last_update > min_update_interval:
- last_update = cur_time
+ # If 'generate_func' takes less than 0.001 seconds to yield the next token
+ # (equivalent to more than 1000 tok/s), assume that the UI is lagging behind and skip yielding
+ if (cur_time - last_update) > latency_threshold:
yield reply
+ last_update = time.monotonic()
if stop_found or (state['max_tokens_second'] > 0 and shared.stop_everything):
break
diff --git a/modules/ui.py b/modules/ui.py
index 9f4d67cb..422db740 100644
--- a/modules/ui.py
+++ b/modules/ui.py
@@ -194,7 +194,6 @@ def list_interface_input_elements():
'max_new_tokens',
'prompt_lookup_num_tokens',
'max_tokens_second',
- 'max_updates_second',
'do_sample',
'dynamic_temperature',
'temperature_last',
diff --git a/modules/ui_parameters.py b/modules/ui_parameters.py
index 733d0901..84f9fbfc 100644
--- a/modules/ui_parameters.py
+++ b/modules/ui_parameters.py
@@ -71,8 +71,6 @@ def create_ui(default_preset):
shared.gradio['max_new_tokens'] = gr.Slider(minimum=shared.settings['max_new_tokens_min'], maximum=shared.settings['max_new_tokens_max'], value=shared.settings['max_new_tokens'], step=1, label='max_new_tokens', info='⚠️ Setting this too high can cause prompt truncation.')
shared.gradio['prompt_lookup_num_tokens'] = gr.Slider(value=shared.settings['prompt_lookup_num_tokens'], minimum=0, maximum=10, step=1, label='prompt_lookup_num_tokens', info='Activates Prompt Lookup Decoding.')
shared.gradio['max_tokens_second'] = gr.Slider(value=shared.settings['max_tokens_second'], minimum=0, maximum=20, step=1, label='Maximum tokens/second', info='To make text readable in real time.')
- shared.gradio['max_updates_second'] = gr.Slider(value=shared.settings['max_updates_second'], minimum=0, maximum=24, step=1, label='Maximum UI updates/second', info='Set this if you experience lag in the UI during streaming.')
-
with gr.Column():
with gr.Row():
with gr.Column():
diff --git a/user_data/settings-template.yaml b/user_data/settings-template.yaml
index ce0f77e1..db481e84 100644
--- a/user_data/settings-template.yaml
+++ b/user_data/settings-template.yaml
@@ -18,7 +18,6 @@ max_new_tokens_min: 1
max_new_tokens_max: 4096
prompt_lookup_num_tokens: 0
max_tokens_second: 0
-max_updates_second: 12
auto_max_new_tokens: true
ban_eos_token: false
add_bos_token: true
From 67bb6190643a343156cb2b723d4de71aeb3a6819 Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 07:46:33 -0700
Subject: [PATCH 2/8] Render only the last message during streaming
---
js/global_scope_js.js | 16 ++-
js/main.js | 2 +-
modules/chat.py | 2 +-
modules/html_generator.py | 233 ++++++++++++++++++++++----------------
modules/ui_chat.py | 4 +-
5 files changed, 151 insertions(+), 106 deletions(-)
diff --git a/js/global_scope_js.js b/js/global_scope_js.js
index 3274f47e..89498814 100644
--- a/js/global_scope_js.js
+++ b/js/global_scope_js.js
@@ -229,7 +229,7 @@ function removeLastClick() {
document.getElementById("Remove-last").click();
}
-function handleMorphdomUpdate(text) {
+function handleMorphdomUpdate(data) {
// Track open blocks
const openBlocks = new Set();
document.querySelectorAll(".thinking-block").forEach(block => {
@@ -254,9 +254,19 @@ function handleMorphdomUpdate(text) {
}
});
+ var target_element, target_html;
+ if (data.last_message_only) {
+ const childNodes = document.getElementsByClassName("messages")[0].childNodes;
+ target_element = childNodes[childNodes.length - 1];
+ target_html = data.html;
+ } else {
+ target_element = document.getElementById("chat").parentNode;
+ target_html = "
" + data.html + "
";
+ }
+
morphdom(
- document.getElementById("chat").parentNode,
- "" + text + "
",
+ target_element,
+ target_html,
{
onBeforeElUpdated: function(fromEl, toEl) {
// Preserve code highlighting
diff --git a/js/main.js b/js/main.js
index d152a572..05c19571 100644
--- a/js/main.js
+++ b/js/main.js
@@ -184,7 +184,7 @@ const observer = new MutationObserver(function(mutations) {
const prevSibling = lastChild?.previousElementSibling;
if (lastChild && prevSibling) {
lastChild.style.setProperty("margin-bottom",
- `max(0px, calc(max(70vh, 100vh - ${prevSibling.offsetHeight}px - 102px) - ${lastChild.offsetHeight}px))`,
+ `max(0px, calc(max(70vh, 100vh - ${prevSibling.offsetHeight}px - 84px) - ${lastChild.offsetHeight}px))`,
"important"
);
}
diff --git a/modules/chat.py b/modules/chat.py
index 1222d2bb..fff82613 100644
--- a/modules/chat.py
+++ b/modules/chat.py
@@ -825,7 +825,7 @@ def generate_chat_reply_wrapper(text, state, regenerate=False, _continue=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)):
- yield chat_html_wrapper(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu']), history
+ yield chat_html_wrapper(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'], last_message_only=(i > 0)), history
current_time = time.monotonic()
# Save on first iteration or if save_interval seconds have passed
diff --git a/modules/html_generator.py b/modules/html_generator.py
index 03b5d485..2c3a3e70 100644
--- a/modules/html_generator.py
+++ b/modules/html_generator.py
@@ -462,175 +462,210 @@ def actions_html(history, i, role, info_message=""):
f'{version_nav_html}')
-def generate_instruct_html(history):
- output = f''
+def generate_instruct_message_html(role, converted_content, row_internal, i, attachments, actions_html_content):
+ """Generate HTML for a single message in instruct style."""
+ class_name = "user-message" if role == "user" else "assistant-message"
- for i in range(len(history['visible'])):
+ return (
+ f'
'
+ f'
'
+ f'
{converted_content}
'
+ f'{attachments}'
+ f'{actions_html_content}'
+ f'
'
+ f'
'
+ )
+
+
+def generate_cai_message_html(role, converted_content, row_internal, i, attachments, actions_html_content, name, timestamp, img):
+ """Generate HTML for a single message in CAI style."""
+ circle_class = "circle-you" if role == "user" else "circle-bot"
+
+ return (
+ f'
'
+ f'
{img}
'
+ f'
'
+ f'
{name}{timestamp}
'
+ f'
{converted_content}
'
+ f'{attachments}'
+ f'{actions_html_content}'
+ f'
'
+ f'
'
+ )
+
+
+def generate_wpp_message_html(role, converted_content, row_internal, i, attachments, actions_html_content):
+ """Generate HTML for a single message in WPP style."""
+ text_class = "text-you" if role == "user" else "text-bot"
+
+ return (
+ f'
'
+ f'
'
+ f'
{converted_content}
'
+ f'{attachments}'
+ f'{actions_html_content}'
+ f'
'
+ f'
'
+ )
+
+
+def generate_instruct_html(history, last_message_only=False):
+ if not last_message_only:
+ output = f'
'
+ else:
+ output = ""
+
+ # Determine range - either last message only or all messages
+ start_idx = len(history['visible']) - 1 if last_message_only else 0
+ end_idx = len(history['visible'])
+
+ for i in range(start_idx, end_idx):
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]
- # Get timestamps
+ # Get timestamps and attachments
user_timestamp = format_message_timestamp(history, "user", i)
assistant_timestamp = format_message_timestamp(history, "assistant", i)
-
- # Get attachments
user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
# Create info buttons for timestamps if they exist
info_message_user = ""
- if user_timestamp != "":
+ if user_timestamp:
tooltip_text = get_message_tooltip(history, "user", i)
info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
info_message_assistant = ""
- if assistant_timestamp != "":
+ if assistant_timestamp:
tooltip_text = get_message_tooltip(history, "assistant", i)
info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
- if converted_visible[0]: # Don't display empty user messages
- output += (
- f'
'
- f'
'
- f'
{converted_visible[0]}
'
- f'{user_attachments}'
- f'{actions_html(history, i, "user", info_message_user)}'
- f'
'
- f'
'
+ # Generate user message if not empty
+ if converted_visible[0] and not last_message_only:
+ output += generate_instruct_message_html(
+ "user", converted_visible[0], row_internal[0], i,
+ user_attachments, actions_html(history, i, "user", info_message_user)
)
- output += (
- f'
'
- f'
'
- f'
{converted_visible[1]}
'
- f'{assistant_attachments}'
- f'{actions_html(history, i, "assistant", info_message_assistant)}'
- f'
'
- f'
'
+ # Generate assistant message
+ output += generate_instruct_message_html(
+ "assistant", converted_visible[1], row_internal[1], i,
+ assistant_attachments, actions_html(history, i, "assistant", info_message_assistant)
)
- output += "
"
+ if not last_message_only:
+ output += "
"
+
return output
-def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=False):
- output = f''
+def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=False, last_message_only=False):
+ if not last_message_only:
+ output = f'
'
+ else:
+ output = ""
- # We use ?character and ?time.time() to force the browser to reset caches
+ # Profile images
img_bot = (
f'

'
if Path("user_data/cache/pfp_character_thumb.png").exists() else ''
)
-
img_me = (
f'
 if reset_cache else )
'
if Path("user_data/cache/pfp_me.png").exists() else ''
)
- for i in range(len(history['visible'])):
+ # Determine range - either last message only or all messages
+ start_idx = len(history['visible']) - 1 if last_message_only else 0
+ end_idx = len(history['visible'])
+
+ for i in range(start_idx, end_idx):
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]
- # Get timestamps
+ # Get timestamps and attachments
user_timestamp = format_message_timestamp(history, "user", i, tooltip_include_timestamp=False)
assistant_timestamp = format_message_timestamp(history, "assistant", i, tooltip_include_timestamp=False)
-
- # Get attachments
user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
- if converted_visible[0]: # Don't display empty user messages
- output += (
- f'
'
- f'
{img_me}
'
- f'
'
- f'
{name1}{user_timestamp}
'
- f'
{converted_visible[0]}
'
- f'{user_attachments}'
- f'{actions_html(history, i, "user")}'
- f'
'
- f'
'
+ # Generate user message if not empty
+ if converted_visible[0] and not last_message_only:
+ output += generate_cai_message_html(
+ "user", converted_visible[0], row_internal[0], i,
+ user_attachments, actions_html(history, i, "user"),
+ name1, user_timestamp, img_me
)
- output += (
- f'
'
- f'
{img_bot}
'
- f'
'
- f'
{name2}{assistant_timestamp}
'
- f'
{converted_visible[1]}
'
- f'{assistant_attachments}'
- f'{actions_html(history, i, "assistant")}'
- f'
'
- f'
'
+ # Generate assistant message
+ output += generate_cai_message_html(
+ "assistant", converted_visible[1], row_internal[1], i,
+ assistant_attachments, actions_html(history, i, "assistant"),
+ name2, assistant_timestamp, img_bot
)
- output += "
"
+ if not last_message_only:
+ output += "
"
+
return output
-def generate_chat_html(history, name1, name2, reset_cache=False):
- output = f''
+def generate_chat_html(history, name1, name2, reset_cache=False, last_message_only=False):
+ if not last_message_only:
+ output = f'
'
+ else:
+ output = ""
- for i in range(len(history['visible'])):
+ # Determine range - either last message only or all messages
+ start_idx = len(history['visible']) - 1 if last_message_only else 0
+ end_idx = len(history['visible'])
+
+ for i in range(start_idx, end_idx):
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]
- # Get timestamps
+ # Get timestamps and attachments
user_timestamp = format_message_timestamp(history, "user", i)
assistant_timestamp = format_message_timestamp(history, "assistant", i)
-
- # Get attachments
user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
# Create info buttons for timestamps if they exist
info_message_user = ""
- if user_timestamp != "":
+ if user_timestamp:
tooltip_text = get_message_tooltip(history, "user", i)
info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
info_message_assistant = ""
- if assistant_timestamp != "":
+ if assistant_timestamp:
tooltip_text = get_message_tooltip(history, "assistant", i)
info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
- if converted_visible[0]: # Don't display empty user messages
- output += (
- f'
'
- f'
'
- f'
{converted_visible[0]}
'
- f'{user_attachments}'
- f'{actions_html(history, i, "user", info_message_user)}'
- f'
'
- f'
'
+ # Generate user message if not empty
+ if converted_visible[0] and not last_message_only:
+ output += generate_wpp_message_html(
+ "user", converted_visible[0], row_internal[0], i,
+ user_attachments, actions_html(history, i, "user", info_message_user)
)
- output += (
- f'
'
- f'
'
- f'
{converted_visible[1]}
'
- f'{assistant_attachments}'
- f'{actions_html(history, i, "assistant", info_message_assistant)}'
- f'
'
- f'
'
+ # Generate assistant message
+ output += generate_wpp_message_html(
+ "assistant", converted_visible[1], row_internal[1], i,
+ assistant_attachments, actions_html(history, i, "assistant", info_message_assistant)
)
- output += "
"
+ if not last_message_only:
+ output += "
"
+
return output
@@ -644,15 +679,15 @@ def time_greeting():
return "Good evening!"
-def chat_html_wrapper(history, name1, name2, mode, style, character, reset_cache=False):
+def chat_html_wrapper(history, name1, name2, mode, style, character, reset_cache=False, last_message_only=False):
if len(history['visible']) == 0:
greeting = f"{time_greeting()} How can I help you today?
"
result = f'{greeting}
'
elif mode == 'instruct':
- result = generate_instruct_html(history)
+ result = generate_instruct_html(history, last_message_only=last_message_only)
elif style == 'wpp':
- result = generate_chat_html(history, name1, name2)
+ result = generate_chat_html(history, name1, name2, last_message_only=last_message_only)
else:
- result = generate_cai_chat_html(history, name1, name2, style, character, reset_cache)
+ result = generate_cai_chat_html(history, name1, name2, style, character, reset_cache=reset_cache, last_message_only=last_message_only)
- return {'html': result}
+ return {'html': result, 'last_message_only': last_message_only}
diff --git a/modules/ui_chat.py b/modules/ui_chat.py
index 822b77b8..e71341f1 100644
--- a/modules/ui_chat.py
+++ b/modules/ui_chat.py
@@ -47,7 +47,7 @@ def create_ui():
with gr.Row():
with gr.Column(elem_id='chat-col'):
- shared.gradio['display'] = gr.JSON(value={}, visible=False) # Hidden buffer
+ shared.gradio['display'] = gr.JSON(value={}, visible=True) # Hidden buffer
shared.gradio['html_display'] = gr.HTML(value=chat_html_wrapper({'internal': [], 'visible': [], 'metadata': {}}, '', '', 'chat', 'cai-chat', '')['html'], visible=True)
with gr.Row(elem_id="chat-input-row"):
with gr.Column(scale=1, elem_id='gr-hover-container'):
@@ -195,7 +195,7 @@ def create_event_handlers():
shared.reload_inputs = gradio(reload_arr)
# Morph HTML updates instead of updating everything
- shared.gradio['display'].change(None, gradio('display'), None, js="(data) => handleMorphdomUpdate(data.html)")
+ shared.gradio['display'].change(None, gradio('display'), None, js="(data) => handleMorphdomUpdate(data)")
shared.gradio['Generate'].click(
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
From af947fcbb6c0bc82f301910150e99eac79837d2f Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 08:16:45 -0700
Subject: [PATCH 3/8] Hide the hidden buffer
---
modules/ui_chat.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/ui_chat.py b/modules/ui_chat.py
index e71341f1..24ec5270 100644
--- a/modules/ui_chat.py
+++ b/modules/ui_chat.py
@@ -47,7 +47,7 @@ def create_ui():
with gr.Row():
with gr.Column(elem_id='chat-col'):
- shared.gradio['display'] = gr.JSON(value={}, visible=True) # Hidden buffer
+ shared.gradio['display'] = gr.JSON(value={}, visible=False) # Hidden buffer
shared.gradio['html_display'] = gr.HTML(value=chat_html_wrapper({'internal': [], 'visible': [], 'metadata': {}}, '', '', 'chat', 'cai-chat', '')['html'], visible=True)
with gr.Row(elem_id="chat-input-row"):
with gr.Column(scale=1, elem_id='gr-hover-container'):
From e20989171c80a53b419cdef62da2390f7f7e09ca Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 10:30:31 -0700
Subject: [PATCH 4/8] Make 'history' a gr.State()
---
modules/ui_chat.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/ui_chat.py b/modules/ui_chat.py
index 24ec5270..a8eaadfa 100644
--- a/modules/ui_chat.py
+++ b/modules/ui_chat.py
@@ -18,7 +18,7 @@ def create_ui():
mu = shared.args.multi_user
shared.gradio['Chat input'] = gr.State()
- shared.gradio['history'] = gr.JSON(visible=False)
+ shared.gradio['history'] = gr.State()
with gr.Tab('Chat', id='Chat', elem_id='chat-tab'):
with gr.Row(elem_id='past-chats-row', elem_classes=['pretty_scrollbar']):
From 1a086adec0a546ccfacd5e46163f5bfeb253c27c Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 11:31:07 -0700
Subject: [PATCH 5/8] Avoid converting user messages to markdown when
last_message_only is set
---
modules/html_generator.py | 104 +++++++++++++++++++++++---------------
1 file changed, 64 insertions(+), 40 deletions(-)
diff --git a/modules/html_generator.py b/modules/html_generator.py
index 2c3a3e70..5b694ea4 100644
--- a/modules/html_generator.py
+++ b/modules/html_generator.py
@@ -528,32 +528,40 @@ def generate_instruct_html(history, last_message_only=False):
for i in range(start_idx, end_idx):
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]
- # Get timestamps and attachments
- user_timestamp = format_message_timestamp(history, "user", i)
+ # Only convert what we need
+ if last_message_only:
+ converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
+ else:
+ converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
+
+ # Get assistant timestamps and attachments (always needed)
assistant_timestamp = format_message_timestamp(history, "assistant", i)
- user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
- # Create info buttons for timestamps if they exist
- info_message_user = ""
- if user_timestamp:
- tooltip_text = get_message_tooltip(history, "user", i)
- info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+ # Only compute user-related variables and generate user message when needed
+ if not last_message_only and converted_visible[0]:
+ user_timestamp = format_message_timestamp(history, "user", i)
+ user_attachments = format_message_attachments(history, "user", i)
- info_message_assistant = ""
- if assistant_timestamp:
- tooltip_text = get_message_tooltip(history, "assistant", i)
- info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+ # Create info button for user timestamp if it exists
+ info_message_user = ""
+ if user_timestamp:
+ tooltip_text = get_message_tooltip(history, "user", i)
+ info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
- # Generate user message if not empty
- if converted_visible[0] and not last_message_only:
+ # Generate user message
output += generate_instruct_message_html(
"user", converted_visible[0], row_internal[0], i,
user_attachments, actions_html(history, i, "user", info_message_user)
)
+ # Create info button for assistant timestamp if it exists
+ info_message_assistant = ""
+ if assistant_timestamp:
+ tooltip_text = get_message_tooltip(history, "assistant", i)
+ info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+
# Generate assistant message
output += generate_instruct_message_html(
"assistant", converted_visible[1], row_internal[1], i,
@@ -577,10 +585,6 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
f'
'
if Path("user_data/cache/pfp_character_thumb.png").exists() else ''
)
- img_me = (
- f'
'
- if Path("user_data/cache/pfp_me.png").exists() else ''
- )
# Determine range - either last message only or all messages
start_idx = len(history['visible']) - 1 if last_message_only else 0
@@ -589,16 +593,28 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
for i in range(start_idx, end_idx):
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]
- # Get timestamps and attachments
- user_timestamp = format_message_timestamp(history, "user", i, tooltip_include_timestamp=False)
+ # Only convert what we need
+ if last_message_only:
+ converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
+ else:
+ converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
+
+ # Get assistant timestamps and attachments (always needed)
assistant_timestamp = format_message_timestamp(history, "assistant", i, tooltip_include_timestamp=False)
- user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
- # Generate user message if not empty
- if converted_visible[0] and not last_message_only:
+ # Only compute user-related variables and generate user message when needed
+ if not last_message_only and converted_visible[0]:
+ img_me = (
+ f'
'
+ if Path("user_data/cache/pfp_me.png").exists() else ''
+ )
+
+ user_timestamp = format_message_timestamp(history, "user", i, tooltip_include_timestamp=False)
+ user_attachments = format_message_attachments(history, "user", i)
+
+ # Generate user message
output += generate_cai_message_html(
"user", converted_visible[0], row_internal[0], i,
user_attachments, actions_html(history, i, "user"),
@@ -631,32 +647,40 @@ def generate_chat_html(history, name1, name2, reset_cache=False, last_message_on
for i in range(start_idx, end_idx):
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]
- # Get timestamps and attachments
- user_timestamp = format_message_timestamp(history, "user", i)
+ # Only convert what we need
+ if last_message_only:
+ converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
+ else:
+ converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
+
+ # Get assistant timestamps and attachments (always needed)
assistant_timestamp = format_message_timestamp(history, "assistant", i)
- user_attachments = format_message_attachments(history, "user", i)
assistant_attachments = format_message_attachments(history, "assistant", i)
- # Create info buttons for timestamps if they exist
- info_message_user = ""
- if user_timestamp:
- tooltip_text = get_message_tooltip(history, "user", i)
- info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+ # Only compute user-related variables and generate user message when needed
+ if not last_message_only and converted_visible[0]:
+ user_timestamp = format_message_timestamp(history, "user", i)
+ user_attachments = format_message_attachments(history, "user", i)
- info_message_assistant = ""
- if assistant_timestamp:
- tooltip_text = get_message_tooltip(history, "assistant", i)
- info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+ # Create info button for user timestamp if it exists
+ info_message_user = ""
+ if user_timestamp:
+ tooltip_text = get_message_tooltip(history, "user", i)
+ info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
- # Generate user message if not empty
- if converted_visible[0] and not last_message_only:
+ # Generate user message
output += generate_wpp_message_html(
"user", converted_visible[0], row_internal[0], i,
user_attachments, actions_html(history, i, "user", info_message_user)
)
+ # Create info button for assistant timestamp if it exists
+ info_message_assistant = ""
+ if assistant_timestamp:
+ tooltip_text = get_message_tooltip(history, "assistant", i)
+ info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+
# Generate assistant message
output += generate_wpp_message_html(
"assistant", converted_visible[1], row_internal[1], i,
From 6f25e6d6acd2a442a2cd7bf12c6e7fc3b7b93cb5 Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 11:40:41 -0700
Subject: [PATCH 6/8] Further optimize handleMorphdomUpdate a bit
---
js/global_scope_js.js | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/js/global_scope_js.js b/js/global_scope_js.js
index 89498814..d5140c93 100644
--- a/js/global_scope_js.js
+++ b/js/global_scope_js.js
@@ -230,9 +230,22 @@ function removeLastClick() {
}
function handleMorphdomUpdate(data) {
+ // Determine target element and use it as query scope
+ var target_element, target_html;
+ if (data.last_message_only) {
+ const childNodes = document.getElementsByClassName("messages")[0].childNodes;
+ target_element = childNodes[childNodes.length - 1];
+ target_html = data.html;
+ } else {
+ target_element = document.getElementById("chat").parentNode;
+ target_html = "" + data.html + "
";
+ }
+
+ const queryScope = target_element;
+
// Track open blocks
const openBlocks = new Set();
- document.querySelectorAll(".thinking-block").forEach(block => {
+ queryScope.querySelectorAll(".thinking-block").forEach(block => {
const blockId = block.getAttribute("data-block-id");
// If block exists and is open, add to open set
if (blockId && block.hasAttribute("open")) {
@@ -242,7 +255,7 @@ function handleMorphdomUpdate(data) {
// Store scroll positions for any open blocks
const scrollPositions = {};
- document.querySelectorAll(".thinking-block[open]").forEach(block => {
+ queryScope.querySelectorAll(".thinking-block[open]").forEach(block => {
const content = block.querySelector(".thinking-content");
const blockId = block.getAttribute("data-block-id");
if (content && blockId) {
@@ -254,16 +267,6 @@ function handleMorphdomUpdate(data) {
}
});
- var target_element, target_html;
- if (data.last_message_only) {
- const childNodes = document.getElementsByClassName("messages")[0].childNodes;
- target_element = childNodes[childNodes.length - 1];
- target_html = data.html;
- } else {
- target_element = document.getElementById("chat").parentNode;
- target_html = "" + data.html + "
";
- }
-
morphdom(
target_element,
target_html,
@@ -317,7 +320,7 @@ function handleMorphdomUpdate(data) {
);
// Add toggle listeners for new blocks
- document.querySelectorAll(".thinking-block").forEach(block => {
+ queryScope.querySelectorAll(".thinking-block").forEach(block => {
if (!block._hasToggleListener) {
block.addEventListener("toggle", function(e) {
if (this.open) {
From e3274bf87120fc699d3bbb00883b7e3bafeca2c1 Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 13:41:25 -0700
Subject: [PATCH 7/8] Simplify the changes to html_generator.py
---
modules/html_generator.py | 241 +++++++++++++++-----------------------
1 file changed, 97 insertions(+), 144 deletions(-)
diff --git a/modules/html_generator.py b/modules/html_generator.py
index 5b694ea4..d47360d3 100644
--- a/modules/html_generator.py
+++ b/modules/html_generator.py
@@ -462,66 +462,39 @@ def actions_html(history, i, role, info_message=""):
f'{version_nav_html}')
-def generate_instruct_message_html(role, converted_content, row_internal, i, attachments, actions_html_content):
- """Generate HTML for a single message in instruct style."""
- class_name = "user-message" if role == "user" else "assistant-message"
-
- return (
- f''
- f'
'
- f'
{converted_content}
'
- f'{attachments}'
- f'{actions_html_content}'
- f'
'
- f'
'
- )
-
-
-def generate_cai_message_html(role, converted_content, row_internal, i, attachments, actions_html_content, name, timestamp, img):
- """Generate HTML for a single message in CAI style."""
- circle_class = "circle-you" if role == "user" else "circle-bot"
-
- return (
- f''
- f'
{img}
'
- f'
'
- f'
{name}{timestamp}
'
- f'
{converted_content}
'
- f'{attachments}'
- f'{actions_html_content}'
- f'
'
- f'
'
- )
-
-
-def generate_wpp_message_html(role, converted_content, row_internal, i, attachments, actions_html_content):
- """Generate HTML for a single message in WPP style."""
- text_class = "text-you" if role == "user" else "text-bot"
-
- return (
- f''
- f'
'
- f'
{converted_content}
'
- f'{attachments}'
- f'{actions_html_content}'
- f'
'
- f'
'
- )
-
-
def generate_instruct_html(history, last_message_only=False):
if not last_message_only:
output = f''
else:
output = ""
- # Determine range - either last message only or all messages
+ def create_message(role, content, raw_content):
+ """Inner function that captures variables from outer scope."""
+ class_name = "user-message" if role == "user" else "assistant-message"
+
+ # Get role-specific data
+ timestamp = format_message_timestamp(history, role, i)
+ attachments = format_message_attachments(history, role, i)
+
+ # Create info button if timestamp exists
+ info_message = ""
+ if timestamp:
+ tooltip_text = get_message_tooltip(history, role, i)
+ info_message = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+
+ return (
+ f'
'
+ f'
'
+ f'
{content}
'
+ f'{attachments}'
+ f'{actions_html(history, i, role, info_message)}'
+ f'
'
+ f'
'
+ )
+
+ # Determine range
start_idx = len(history['visible']) - 1 if last_message_only else 0
end_idx = len(history['visible'])
@@ -529,44 +502,17 @@ def generate_instruct_html(history, last_message_only=False):
row_visible = history['visible'][i]
row_internal = history['internal'][i]
- # Only convert what we need
+ # Convert content
if last_message_only:
converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
else:
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
- # Get assistant timestamps and attachments (always needed)
- assistant_timestamp = format_message_timestamp(history, "assistant", i)
- assistant_attachments = format_message_attachments(history, "assistant", i)
-
- # Only compute user-related variables and generate user message when needed
+ # Generate messages
if not last_message_only and converted_visible[0]:
- user_timestamp = format_message_timestamp(history, "user", i)
- user_attachments = format_message_attachments(history, "user", i)
+ output += create_message("user", converted_visible[0], row_internal[0])
- # Create info button for user timestamp if it exists
- info_message_user = ""
- if user_timestamp:
- tooltip_text = get_message_tooltip(history, "user", i)
- info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
-
- # Generate user message
- output += generate_instruct_message_html(
- "user", converted_visible[0], row_internal[0], i,
- user_attachments, actions_html(history, i, "user", info_message_user)
- )
-
- # Create info button for assistant timestamp if it exists
- info_message_assistant = ""
- if assistant_timestamp:
- tooltip_text = get_message_tooltip(history, "assistant", i)
- info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
-
- # Generate assistant message
- output += generate_instruct_message_html(
- "assistant", converted_visible[1], row_internal[1], i,
- assistant_attachments, actions_html(history, i, "assistant", info_message_assistant)
- )
+ output += create_message("assistant", converted_visible[1], row_internal[1])
if not last_message_only:
output += "
"
@@ -586,7 +532,37 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
if Path("user_data/cache/pfp_character_thumb.png").exists() else ''
)
- # Determine range - either last message only or all messages
+ def create_message(role, content, raw_content):
+ """Inner function for CAI-style messages."""
+ circle_class = "circle-you" if role == "user" else "circle-bot"
+ name = name1 if role == "user" else name2
+
+ # Get role-specific data
+ timestamp = format_message_timestamp(history, role, i, tooltip_include_timestamp=False)
+ attachments = format_message_attachments(history, role, i)
+
+ # Get appropriate image
+ if role == "user":
+ img = (f'
'
+ if Path("user_data/cache/pfp_me.png").exists() else '')
+ else:
+ img = img_bot
+
+ return (
+ f''
+ f'
{img}
'
+ f'
'
+ f'
{name}{timestamp}
'
+ f'
{content}
'
+ f'{attachments}'
+ f'{actions_html(history, i, role)}'
+ f'
'
+ f'
'
+ )
+
+ # Determine range
start_idx = len(history['visible']) - 1 if last_message_only else 0
end_idx = len(history['visible'])
@@ -594,39 +570,17 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
row_visible = history['visible'][i]
row_internal = history['internal'][i]
- # Only convert what we need
+ # Convert content
if last_message_only:
converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
else:
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
- # Get assistant timestamps and attachments (always needed)
- assistant_timestamp = format_message_timestamp(history, "assistant", i, tooltip_include_timestamp=False)
- assistant_attachments = format_message_attachments(history, "assistant", i)
-
- # Only compute user-related variables and generate user message when needed
+ # Generate messages
if not last_message_only and converted_visible[0]:
- img_me = (
- f'
'
- if Path("user_data/cache/pfp_me.png").exists() else ''
- )
+ output += create_message("user", converted_visible[0], row_internal[0])
- user_timestamp = format_message_timestamp(history, "user", i, tooltip_include_timestamp=False)
- user_attachments = format_message_attachments(history, "user", i)
-
- # Generate user message
- output += generate_cai_message_html(
- "user", converted_visible[0], row_internal[0], i,
- user_attachments, actions_html(history, i, "user"),
- name1, user_timestamp, img_me
- )
-
- # Generate assistant message
- output += generate_cai_message_html(
- "assistant", converted_visible[1], row_internal[1], i,
- assistant_attachments, actions_html(history, i, "assistant"),
- name2, assistant_timestamp, img_bot
- )
+ output += create_message("assistant", converted_visible[1], row_internal[1])
if not last_message_only:
output += ""
@@ -640,7 +594,33 @@ def generate_chat_html(history, name1, name2, reset_cache=False, last_message_on
else:
output = ""
- # Determine range - either last message only or all messages
+ def create_message(role, content, raw_content):
+ """Inner function for WPP-style messages."""
+ text_class = "text-you" if role == "user" else "text-bot"
+
+ # Get role-specific data
+ timestamp = format_message_timestamp(history, role, i)
+ attachments = format_message_attachments(history, role, i)
+
+ # Create info button if timestamp exists
+ info_message = ""
+ if timestamp:
+ tooltip_text = get_message_tooltip(history, role, i)
+ info_message = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
+
+ return (
+ f''
+ f'
'
+ f'
{content}
'
+ f'{attachments}'
+ f'{actions_html(history, i, role, info_message)}'
+ f'
'
+ f'
'
+ )
+
+ # Determine range
start_idx = len(history['visible']) - 1 if last_message_only else 0
end_idx = len(history['visible'])
@@ -648,44 +628,17 @@ def generate_chat_html(history, name1, name2, reset_cache=False, last_message_on
row_visible = history['visible'][i]
row_internal = history['internal'][i]
- # Only convert what we need
+ # Convert content
if last_message_only:
converted_visible = [None, convert_to_markdown_wrapped(row_visible[1], message_id=i, use_cache=i != len(history['visible']) - 1)]
else:
converted_visible = [convert_to_markdown_wrapped(entry, message_id=i, use_cache=i != len(history['visible']) - 1) for entry in row_visible]
- # Get assistant timestamps and attachments (always needed)
- assistant_timestamp = format_message_timestamp(history, "assistant", i)
- assistant_attachments = format_message_attachments(history, "assistant", i)
-
- # Only compute user-related variables and generate user message when needed
+ # Generate messages
if not last_message_only and converted_visible[0]:
- user_timestamp = format_message_timestamp(history, "user", i)
- user_attachments = format_message_attachments(history, "user", i)
+ output += create_message("user", converted_visible[0], row_internal[0])
- # Create info button for user timestamp if it exists
- info_message_user = ""
- if user_timestamp:
- tooltip_text = get_message_tooltip(history, "user", i)
- info_message_user = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
-
- # Generate user message
- output += generate_wpp_message_html(
- "user", converted_visible[0], row_internal[0], i,
- user_attachments, actions_html(history, i, "user", info_message_user)
- )
-
- # Create info button for assistant timestamp if it exists
- info_message_assistant = ""
- if assistant_timestamp:
- tooltip_text = get_message_tooltip(history, "assistant", i)
- info_message_assistant = info_button.replace('title="message"', f'title="{html.escape(tooltip_text)}"')
-
- # Generate assistant message
- output += generate_wpp_message_html(
- "assistant", converted_visible[1], row_internal[1], i,
- assistant_attachments, actions_html(history, i, "assistant", info_message_assistant)
- )
+ output += create_message("assistant", converted_visible[1], row_internal[1])
if not last_message_only:
output += ""
From 2d049481927ea7d32d324ec78064f104df6ffe9b Mon Sep 17 00:00:00 2001
From: oobabooga <112222186+oobabooga@users.noreply.github.com>
Date: Sun, 1 Jun 2025 13:42:56 -0700
Subject: [PATCH 8/8] Change a comment
---
modules/html_generator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/html_generator.py b/modules/html_generator.py
index d47360d3..f90e3b04 100644
--- a/modules/html_generator.py
+++ b/modules/html_generator.py
@@ -526,7 +526,7 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
else:
output = ""
- # Profile images
+ # We use ?character and ?time.time() to force the browser to reset caches
img_bot = (
f'
'
if Path("user_data/cache/pfp_character_thumb.png").exists() else ''