mirror of
https://github.com/oobabooga/text-generation-webui.git
synced 2025-06-07 22:25:54 -04:00
Add attachments support (text files, PDF documents) (#7005)
This commit is contained in:
parent
5d00574a56
commit
409a48d6bd
22 changed files with 233 additions and 12 deletions
56
css/main.css
56
css/main.css
|
@ -592,6 +592,7 @@ div.svelte-362y77>*, div.svelte-362y77>.form>* {
|
||||||
padding: 0.65rem 2.5rem;
|
padding: 0.65rem 2.5rem;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: 0;
|
box-shadow: 0;
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat-input textarea::placeholder {
|
#chat-input textarea::placeholder {
|
||||||
|
@ -611,6 +612,16 @@ div.svelte-362y77>*, div.svelte-362y77>.form>* {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#chat-input .submit-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-input .upload-button {
|
||||||
|
margin-right: 16px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-input-positioned {
|
.chat-input-positioned {
|
||||||
max-width: 54rem;
|
max-width: 54rem;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -1395,3 +1406,48 @@ strong {
|
||||||
.dark #vram-info .value {
|
.dark #vram-info .value {
|
||||||
color: #07ff07;
|
color: #07ff07;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-attachments {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px;
|
||||||
|
background: rgb(0 0 0 / 5%);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgb(0 0 0 / 10%);
|
||||||
|
min-width: 80px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-icon {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-name {
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .attachment-box {
|
||||||
|
background: rgb(255 255 255 / 5%);
|
||||||
|
border: 1px solid rgb(255 255 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .attachment-icon {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
124
modules/chat.py
124
modules/chat.py
|
@ -157,7 +157,9 @@ def generate_chat_prompt(user_input, state, **kwargs):
|
||||||
impersonate = kwargs.get('impersonate', False)
|
impersonate = kwargs.get('impersonate', False)
|
||||||
_continue = kwargs.get('_continue', False)
|
_continue = kwargs.get('_continue', False)
|
||||||
also_return_rows = kwargs.get('also_return_rows', False)
|
also_return_rows = kwargs.get('also_return_rows', False)
|
||||||
history = kwargs.get('history', state['history'])['internal']
|
history_data = kwargs.get('history', state['history'])
|
||||||
|
history = history_data['internal']
|
||||||
|
metadata = history_data.get('metadata', {})
|
||||||
|
|
||||||
# Templates
|
# Templates
|
||||||
chat_template_str = state['chat_template_str']
|
chat_template_str = state['chat_template_str']
|
||||||
|
@ -196,11 +198,13 @@ def generate_chat_prompt(user_input, state, **kwargs):
|
||||||
messages.append({"role": "system", "content": context})
|
messages.append({"role": "system", "content": context})
|
||||||
|
|
||||||
insert_pos = len(messages)
|
insert_pos = len(messages)
|
||||||
for entry in reversed(history):
|
for i, entry in enumerate(reversed(history)):
|
||||||
user_msg = entry[0].strip()
|
user_msg = entry[0].strip()
|
||||||
assistant_msg = entry[1].strip()
|
assistant_msg = entry[1].strip()
|
||||||
tool_msg = entry[2].strip() if len(entry) > 2 else ''
|
tool_msg = entry[2].strip() if len(entry) > 2 else ''
|
||||||
|
|
||||||
|
row_idx = len(history) - i - 1
|
||||||
|
|
||||||
if tool_msg:
|
if tool_msg:
|
||||||
messages.insert(insert_pos, {"role": "tool", "content": tool_msg})
|
messages.insert(insert_pos, {"role": "tool", "content": tool_msg})
|
||||||
|
|
||||||
|
@ -208,10 +212,40 @@ def generate_chat_prompt(user_input, state, **kwargs):
|
||||||
messages.insert(insert_pos, {"role": "assistant", "content": assistant_msg})
|
messages.insert(insert_pos, {"role": "assistant", "content": assistant_msg})
|
||||||
|
|
||||||
if user_msg not in ['', '<|BEGIN-VISIBLE-CHAT|>']:
|
if user_msg not in ['', '<|BEGIN-VISIBLE-CHAT|>']:
|
||||||
messages.insert(insert_pos, {"role": "user", "content": user_msg})
|
# Check for user message attachments in metadata
|
||||||
|
user_key = f"user_{row_idx}"
|
||||||
|
enhanced_user_msg = user_msg
|
||||||
|
|
||||||
|
# Add attachment content if present
|
||||||
|
if user_key in metadata and "attachments" in metadata[user_key]:
|
||||||
|
attachments_text = ""
|
||||||
|
for attachment in metadata[user_key]["attachments"]:
|
||||||
|
filename = attachment.get("name", "file")
|
||||||
|
content = attachment.get("content", "")
|
||||||
|
attachments_text += f"\nName: {filename}\nContents:\n\n=====\n{content}\n=====\n\n"
|
||||||
|
|
||||||
|
if attachments_text:
|
||||||
|
enhanced_user_msg = f"{user_msg}\n\nATTACHMENTS:{attachments_text}"
|
||||||
|
|
||||||
|
messages.insert(insert_pos, {"role": "user", "content": enhanced_user_msg})
|
||||||
|
|
||||||
user_input = user_input.strip()
|
user_input = user_input.strip()
|
||||||
if user_input and not impersonate and not _continue:
|
if user_input and not impersonate and not _continue:
|
||||||
|
# For the current user input being processed, check if we need to add attachments
|
||||||
|
if not impersonate and not _continue and len(history_data.get('metadata', {})) > 0:
|
||||||
|
current_row_idx = len(history)
|
||||||
|
user_key = f"user_{current_row_idx}"
|
||||||
|
|
||||||
|
if user_key in metadata and "attachments" in metadata[user_key]:
|
||||||
|
attachments_text = ""
|
||||||
|
for attachment in metadata[user_key]["attachments"]:
|
||||||
|
filename = attachment.get("name", "file")
|
||||||
|
content = attachment.get("content", "")
|
||||||
|
attachments_text += f"\nName: {filename}\nContents:\n\n=====\n{content}\n=====\n\n"
|
||||||
|
|
||||||
|
if attachments_text:
|
||||||
|
user_input = f"{user_input}\n\nATTACHMENTS:{attachments_text}"
|
||||||
|
|
||||||
messages.append({"role": "user", "content": user_input})
|
messages.append({"role": "user", "content": user_input})
|
||||||
|
|
||||||
def make_prompt(messages):
|
def make_prompt(messages):
|
||||||
|
@ -280,7 +314,6 @@ def generate_chat_prompt(user_input, state, **kwargs):
|
||||||
|
|
||||||
# Resort to truncating the user input
|
# Resort to truncating the user input
|
||||||
else:
|
else:
|
||||||
|
|
||||||
user_message = messages[-1]['content']
|
user_message = messages[-1]['content']
|
||||||
|
|
||||||
# Bisect the truncation point
|
# Bisect the truncation point
|
||||||
|
@ -393,7 +426,74 @@ def add_message_version(history, row_idx, is_current=True):
|
||||||
history['metadata'][key]["current_version_index"] = len(history['metadata'][key]["versions"]) - 1
|
history['metadata'][key]["current_version_index"] = len(history['metadata'][key]["versions"]) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def add_message_attachment(history, row_idx, file_path, is_user=True):
|
||||||
|
"""Add a file attachment to a message in history metadata"""
|
||||||
|
if 'metadata' not in history:
|
||||||
|
history['metadata'] = {}
|
||||||
|
|
||||||
|
key = f"{'user' if is_user else 'assistant'}_{row_idx}"
|
||||||
|
|
||||||
|
if key not in history['metadata']:
|
||||||
|
history['metadata'][key] = {"timestamp": get_current_timestamp()}
|
||||||
|
if "attachments" not in history['metadata'][key]:
|
||||||
|
history['metadata'][key]["attachments"] = []
|
||||||
|
|
||||||
|
# Get file info using pathlib
|
||||||
|
path = Path(file_path)
|
||||||
|
filename = path.name
|
||||||
|
file_extension = path.suffix.lower()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Handle different file types
|
||||||
|
if file_extension == '.pdf':
|
||||||
|
# Process PDF file
|
||||||
|
content = extract_pdf_text(path)
|
||||||
|
file_type = "application/pdf"
|
||||||
|
else:
|
||||||
|
# Default handling for text files
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
file_type = "text/plain"
|
||||||
|
|
||||||
|
# Add attachment
|
||||||
|
attachment = {
|
||||||
|
"name": filename,
|
||||||
|
"type": file_type,
|
||||||
|
"content": content,
|
||||||
|
}
|
||||||
|
|
||||||
|
history['metadata'][key]["attachments"].append(attachment)
|
||||||
|
return content # Return the content for reuse
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing attachment {filename}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_pdf_text(pdf_path):
|
||||||
|
"""Extract text from a PDF file"""
|
||||||
|
import PyPDF2
|
||||||
|
|
||||||
|
text = ""
|
||||||
|
try:
|
||||||
|
with open(pdf_path, 'rb') as file:
|
||||||
|
pdf_reader = PyPDF2.PdfReader(file)
|
||||||
|
for page_num in range(len(pdf_reader.pages)):
|
||||||
|
page = pdf_reader.pages[page_num]
|
||||||
|
text += page.extract_text() + "\n\n"
|
||||||
|
|
||||||
|
return text.strip()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error extracting text from PDF: {e}")
|
||||||
|
return f"[Error extracting PDF text: {str(e)}]"
|
||||||
|
|
||||||
|
|
||||||
def chatbot_wrapper(text, state, regenerate=False, _continue=False, loading_message=True, for_ui=False):
|
def chatbot_wrapper(text, state, regenerate=False, _continue=False, loading_message=True, for_ui=False):
|
||||||
|
# Handle dict format with text and files
|
||||||
|
files = []
|
||||||
|
if isinstance(text, dict):
|
||||||
|
files = text.get('files', [])
|
||||||
|
text = text.get('text', '')
|
||||||
|
|
||||||
history = state['history']
|
history = state['history']
|
||||||
output = copy.deepcopy(history)
|
output = copy.deepcopy(history)
|
||||||
output = apply_extensions('history', output)
|
output = apply_extensions('history', output)
|
||||||
|
@ -411,12 +511,18 @@ def chatbot_wrapper(text, state, regenerate=False, _continue=False, loading_mess
|
||||||
if not (regenerate or _continue):
|
if not (regenerate or _continue):
|
||||||
visible_text = html.escape(text)
|
visible_text = html.escape(text)
|
||||||
|
|
||||||
|
# Process file attachments and store in metadata
|
||||||
|
row_idx = len(output['internal'])
|
||||||
|
|
||||||
|
# Add attachments to metadata only, not modifying the message text
|
||||||
|
for file_path in files:
|
||||||
|
add_message_attachment(output, row_idx, file_path, is_user=True)
|
||||||
|
|
||||||
# Apply extensions
|
# Apply extensions
|
||||||
text, visible_text = apply_extensions('chat_input', text, visible_text, state)
|
text, visible_text = apply_extensions('chat_input', text, visible_text, state)
|
||||||
text = apply_extensions('input', text, state, is_chat=True)
|
text = apply_extensions('input', text, state, is_chat=True)
|
||||||
|
|
||||||
# Current row index
|
# Current row index
|
||||||
row_idx = len(output['internal'])
|
|
||||||
output['internal'].append([text, ''])
|
output['internal'].append([text, ''])
|
||||||
output['visible'].append([visible_text, ''])
|
output['visible'].append([visible_text, ''])
|
||||||
# Add metadata with timestamp
|
# Add metadata with timestamp
|
||||||
|
@ -1215,7 +1321,7 @@ def handle_replace_last_reply_click(text, state):
|
||||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||||
|
|
||||||
return [history, html, ""]
|
return [history, html, {"text": "", "files": []}]
|
||||||
|
|
||||||
|
|
||||||
def handle_send_dummy_message_click(text, state):
|
def handle_send_dummy_message_click(text, state):
|
||||||
|
@ -1223,7 +1329,7 @@ def handle_send_dummy_message_click(text, state):
|
||||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||||
|
|
||||||
return [history, html, ""]
|
return [history, html, {"text": "", "files": []}]
|
||||||
|
|
||||||
|
|
||||||
def handle_send_dummy_reply_click(text, state):
|
def handle_send_dummy_reply_click(text, state):
|
||||||
|
@ -1231,7 +1337,7 @@ def handle_send_dummy_reply_click(text, state):
|
||||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||||
|
|
||||||
return [history, html, ""]
|
return [history, html, {"text": "", "files": []}]
|
||||||
|
|
||||||
|
|
||||||
def handle_remove_last_click(state):
|
def handle_remove_last_click(state):
|
||||||
|
@ -1239,7 +1345,7 @@ def handle_remove_last_click(state):
|
||||||
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
save_history(history, state['unique_id'], state['character_menu'], state['mode'])
|
||||||
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
html = redraw_html(history, state['name1'], state['name2'], state['mode'], state['chat_style'], state['character_menu'])
|
||||||
|
|
||||||
return [history, html, last_input]
|
return [history, html, {"text": last_input, "files": []}]
|
||||||
|
|
||||||
|
|
||||||
def handle_unique_id_select(state):
|
def handle_unique_id_select(state):
|
||||||
|
|
|
@ -338,6 +338,7 @@ remove_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20
|
||||||
branch_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-git-branch"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 8l0 8" /><path d="M9 18h6a2 2 0 0 0 2 -2v-5" /><path d="M14 14l3 -3l3 3" /></svg>'''
|
branch_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-git-branch"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M7 8l0 8" /><path d="M9 18h6a2 2 0 0 0 2 -2v-5" /><path d="M14 14l3 -3l3 3" /></svg>'''
|
||||||
info_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
|
info_svg = '''<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
|
||||||
info_svg_small = '''<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" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
|
info_svg_small = '''<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" class="thinking-icon tabler-icon tabler-icon-info-circle"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2a10 10 0 0 1 0 20a10 10 0 0 1 0 -20z" /><path d="M12 16v-4" /><path d="M12 8h.01" /></svg>'''
|
||||||
|
attachment_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"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.48-8.48l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>'''
|
||||||
|
|
||||||
copy_button = f'<button class="footer-button footer-copy-button" title="Copy" onclick="copyToClipboard(this)">{copy_svg}</button>'
|
copy_button = f'<button class="footer-button footer-copy-button" title="Copy" onclick="copyToClipboard(this)">{copy_svg}</button>'
|
||||||
branch_button = f'<button class="footer-button footer-branch-button" title="Branch here" onclick="branchHere(this)">{branch_svg}</button>'
|
branch_button = f'<button class="footer-button footer-branch-button" title="Branch here" onclick="branchHere(this)">{branch_svg}</button>'
|
||||||
|
@ -357,6 +358,28 @@ def format_message_timestamp(history, role, index):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def format_message_attachments(history, role, index):
|
||||||
|
"""Get formatted HTML for message attachments if available"""
|
||||||
|
key = f"{role}_{index}"
|
||||||
|
if 'metadata' in history and key in history['metadata'] and 'attachments' in history['metadata'][key]:
|
||||||
|
attachments = history['metadata'][key]['attachments']
|
||||||
|
if not attachments:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
attachments_html = '<div class="message-attachments">'
|
||||||
|
for attachment in attachments:
|
||||||
|
attachments_html += (
|
||||||
|
f'<div class="attachment-box">'
|
||||||
|
f'<div class="attachment-icon">{attachment_svg}</div>'
|
||||||
|
f'<div class="attachment-name">{html.escape(attachment["name"])}</div>'
|
||||||
|
f'</div>'
|
||||||
|
)
|
||||||
|
attachments_html += '</div>'
|
||||||
|
return attachments_html
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def actions_html(history, i, info_message=""):
|
def actions_html(history, i, info_message=""):
|
||||||
return (f'<div class="message-actions">'
|
return (f'<div class="message-actions">'
|
||||||
f'{copy_button}'
|
f'{copy_button}'
|
||||||
|
@ -380,6 +403,10 @@ def generate_instruct_html(history):
|
||||||
user_timestamp = format_message_timestamp(history, "user", i)
|
user_timestamp = format_message_timestamp(history, "user", i)
|
||||||
assistant_timestamp = format_message_timestamp(history, "assistant", 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
|
# Create info buttons for timestamps if they exist
|
||||||
info_message_user = ""
|
info_message_user = ""
|
||||||
if user_timestamp != "":
|
if user_timestamp != "":
|
||||||
|
@ -399,6 +426,7 @@ def generate_instruct_html(history):
|
||||||
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
||||||
f'<div class="text">'
|
f'<div class="text">'
|
||||||
f'<div class="message-body">{converted_visible[0]}</div>'
|
f'<div class="message-body">{converted_visible[0]}</div>'
|
||||||
|
f'{user_attachments}'
|
||||||
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
|
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
@ -410,6 +438,7 @@ def generate_instruct_html(history):
|
||||||
f'data-index={i}>'
|
f'data-index={i}>'
|
||||||
f'<div class="text">'
|
f'<div class="text">'
|
||||||
f'<div class="message-body">{converted_visible[1]}</div>'
|
f'<div class="message-body">{converted_visible[1]}</div>'
|
||||||
|
f'{assistant_attachments}'
|
||||||
f'{actions_html(history, i, info_message_assistant)}'
|
f'{actions_html(history, i, info_message_assistant)}'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
@ -442,6 +471,10 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
||||||
user_timestamp = format_message_timestamp(history, "user", i)
|
user_timestamp = format_message_timestamp(history, "user", i)
|
||||||
assistant_timestamp = format_message_timestamp(history, "assistant", 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)
|
||||||
|
|
||||||
if converted_visible[0]: # Don't display empty user messages
|
if converted_visible[0]: # Don't display empty user messages
|
||||||
output += (
|
output += (
|
||||||
f'<div class="message" '
|
f'<div class="message" '
|
||||||
|
@ -450,6 +483,7 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
||||||
f'<div class="text">'
|
f'<div class="text">'
|
||||||
f'<div class="username">{name1}{user_timestamp}</div>'
|
f'<div class="username">{name1}{user_timestamp}</div>'
|
||||||
f'<div class="message-body">{converted_visible[0]}</div>'
|
f'<div class="message-body">{converted_visible[0]}</div>'
|
||||||
|
f'{user_attachments}'
|
||||||
f'<div class="message-actions">{copy_button}</div>'
|
f'<div class="message-actions">{copy_button}</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
@ -463,6 +497,7 @@ def generate_cai_chat_html(history, name1, name2, style, character, reset_cache=
|
||||||
f'<div class="text">'
|
f'<div class="text">'
|
||||||
f'<div class="username">{name2}{assistant_timestamp}</div>'
|
f'<div class="username">{name2}{assistant_timestamp}</div>'
|
||||||
f'<div class="message-body">{converted_visible[1]}</div>'
|
f'<div class="message-body">{converted_visible[1]}</div>'
|
||||||
|
f'{assistant_attachments}'
|
||||||
f'{actions_html(history, i)}'
|
f'{actions_html(history, i)}'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
@ -484,6 +519,10 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
||||||
user_timestamp = format_message_timestamp(history, "user", i)
|
user_timestamp = format_message_timestamp(history, "user", i)
|
||||||
assistant_timestamp = format_message_timestamp(history, "assistant", 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
|
# Create info buttons for timestamps if they exist
|
||||||
info_message_user = ""
|
info_message_user = ""
|
||||||
if user_timestamp != "":
|
if user_timestamp != "":
|
||||||
|
@ -503,6 +542,7 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
||||||
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
f'data-raw="{html.escape(row_internal[0], quote=True)}">'
|
||||||
f'<div class="text-you">'
|
f'<div class="text-you">'
|
||||||
f'<div class="message-body">{converted_visible[0]}</div>'
|
f'<div class="message-body">{converted_visible[0]}</div>'
|
||||||
|
f'{user_attachments}'
|
||||||
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
|
f'<div class="message-actions">{copy_button}{info_message_user}</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
@ -514,6 +554,7 @@ def generate_chat_html(history, name1, name2, reset_cache=False):
|
||||||
f'data-index={i}>'
|
f'data-index={i}>'
|
||||||
f'<div class="text-bot">'
|
f'<div class="text-bot">'
|
||||||
f'<div class="message-body">{converted_visible[1]}</div>'
|
f'<div class="message-body">{converted_visible[1]}</div>'
|
||||||
|
f'{assistant_attachments}'
|
||||||
f'{actions_html(history, i, info_message_assistant)}'
|
f'{actions_html(history, i, info_message_assistant)}'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
f'</div>'
|
f'</div>'
|
||||||
|
|
|
@ -54,7 +54,7 @@ def create_ui():
|
||||||
gr.HTML(value='<div class="hover-element" onclick="void(0)"><span style="width: 100px; display: block" id="hover-element-button">☰</span><div class="hover-menu" id="hover-menu"></div>', elem_id='gr-hover')
|
gr.HTML(value='<div class="hover-element" onclick="void(0)"><span style="width: 100px; display: block" id="hover-element-button">☰</span><div class="hover-menu" id="hover-menu"></div>', elem_id='gr-hover')
|
||||||
|
|
||||||
with gr.Column(scale=10, elem_id='chat-input-container'):
|
with gr.Column(scale=10, elem_id='chat-input-container'):
|
||||||
shared.gradio['textbox'] = gr.Textbox(label='', placeholder='Send a message', elem_id='chat-input', elem_classes=['add_scrollbar'])
|
shared.gradio['textbox'] = gr.MultimodalTextbox(label='', placeholder='Send a message', file_types=['text', '.pdf'], elem_id='chat-input', elem_classes=['add_scrollbar'])
|
||||||
shared.gradio['show_controls'] = gr.Checkbox(value=shared.settings['show_controls'], label='Show controls (Ctrl+S)', elem_id='show-controls')
|
shared.gradio['show_controls'] = gr.Checkbox(value=shared.settings['show_controls'], label='Show controls (Ctrl+S)', elem_id='show-controls')
|
||||||
shared.gradio['typing-dots'] = gr.HTML(value='<div class="typing"><span></span><span class="dot1"></span><span class="dot2"></span></div>', label='typing', elem_id='typing-container')
|
shared.gradio['typing-dots'] = gr.HTML(value='<div class="typing"><span></span><span class="dot1"></span><span class="dot2"></span></div>', label='typing', elem_id='typing-container')
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ def create_event_handlers():
|
||||||
|
|
||||||
shared.gradio['Generate'].click(
|
shared.gradio['Generate'].click(
|
||||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||||
lambda x: (x, ''), gradio('textbox'), gradio('Chat input', 'textbox'), show_progress=False).then(
|
lambda x: (x, {"text": "", "files": []}), gradio('textbox'), gradio('Chat input', 'textbox'), show_progress=False).then(
|
||||||
lambda: None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.add("_generating")').then(
|
lambda: None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.add("_generating")').then(
|
||||||
chat.generate_chat_reply_wrapper, gradio(inputs), gradio('display', 'history'), show_progress=False).then(
|
chat.generate_chat_reply_wrapper, gradio(inputs), gradio('display', 'history'), show_progress=False).then(
|
||||||
None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.remove("_generating")').then(
|
None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.remove("_generating")').then(
|
||||||
|
@ -194,7 +194,7 @@ def create_event_handlers():
|
||||||
|
|
||||||
shared.gradio['textbox'].submit(
|
shared.gradio['textbox'].submit(
|
||||||
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
ui.gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then(
|
||||||
lambda x: (x, ''), gradio('textbox'), gradio('Chat input', 'textbox'), show_progress=False).then(
|
lambda x: (x, {"text": "", "files": []}), gradio('textbox'), gradio('Chat input', 'textbox'), show_progress=False).then(
|
||||||
lambda: None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.add("_generating")').then(
|
lambda: None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.add("_generating")').then(
|
||||||
chat.generate_chat_reply_wrapper, gradio(inputs), gradio('display', 'history'), show_progress=False).then(
|
chat.generate_chat_reply_wrapper, gradio(inputs), gradio('display', 'history'), show_progress=False).then(
|
||||||
None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.remove("_generating")').then(
|
None, None, None, js='() => document.getElementById("chat").parentNode.parentNode.parentNode.classList.remove("_generating")').then(
|
||||||
|
|
|
@ -13,6 +13,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -13,6 +13,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -12,6 +12,7 @@ peft==0.15.*
|
||||||
Pillow>=9.5.0
|
Pillow>=9.5.0
|
||||||
psutil
|
psutil
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
|
@ -4,6 +4,7 @@ jinja2==3.1.6
|
||||||
markdown
|
markdown
|
||||||
numpy==1.26.*
|
numpy==1.26.*
|
||||||
pydantic==2.8.2
|
pydantic==2.8.2
|
||||||
|
PyPDF2==3.0.1
|
||||||
pyyaml
|
pyyaml
|
||||||
requests
|
requests
|
||||||
rich
|
rich
|
||||||
|
|
Loading…
Add table
Reference in a new issue