mirror of
https://github.com/deepseek-ai/DeepSeek-VL
synced 2025-06-26 18:27:43 +00:00
feat: gradio demo integration (#16)
Co-authored-by: Bo Liu <benjaminliu.eecs@gmail.com> Co-authored-by: Haoyu Lu <ruclhy1998@163.com>
This commit is contained in:
94
deepseek_vl/serve/app_modules/gradio_utils.py
Executable file
94
deepseek_vl/serve/app_modules/gradio_utils.py
Executable file
@@ -0,0 +1,94 @@
|
||||
# Copyright (c) 2023-2024 DeepSeek.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from functools import wraps
|
||||
|
||||
import gradio as gr
|
||||
|
||||
|
||||
def wrap_gen_fn(gen_fn):
|
||||
@wraps(gen_fn)
|
||||
def wrapped_gen_fn(prompt, *args, **kwargs):
|
||||
try:
|
||||
yield from gen_fn(prompt, *args, **kwargs)
|
||||
except gr.Error as g_err:
|
||||
raise g_err
|
||||
except Exception as e:
|
||||
raise gr.Error(f"Failed to generate text: {e}") from e
|
||||
|
||||
return wrapped_gen_fn
|
||||
|
||||
|
||||
def delete_last_conversation(chatbot, history):
|
||||
if len(history) % 2 != 0:
|
||||
gr.Error("history length is not even")
|
||||
return (
|
||||
chatbot,
|
||||
history,
|
||||
"Delete Done",
|
||||
)
|
||||
|
||||
if len(chatbot) > 0:
|
||||
chatbot.pop()
|
||||
|
||||
if len(history) > 0 and len(history) % 2 == 0:
|
||||
history.pop()
|
||||
history.pop()
|
||||
|
||||
return (
|
||||
chatbot,
|
||||
history,
|
||||
"Delete Done",
|
||||
)
|
||||
|
||||
|
||||
def reset_state():
|
||||
return [], [], None, "Reset Done"
|
||||
|
||||
|
||||
def reset_textbox():
|
||||
return gr.update(value=""), ""
|
||||
|
||||
|
||||
def cancel_outputing():
|
||||
return "Stop Done"
|
||||
|
||||
|
||||
def transfer_input(input_text, input_image):
|
||||
print("transferring input text and input image")
|
||||
return (
|
||||
input_text,
|
||||
input_image,
|
||||
gr.update(value=""),
|
||||
gr.update(value=None),
|
||||
gr.Button(visible=True),
|
||||
)
|
||||
|
||||
|
||||
class State:
|
||||
interrupted = False
|
||||
|
||||
def interrupt(self):
|
||||
self.interrupted = True
|
||||
|
||||
def recover(self):
|
||||
self.interrupted = False
|
||||
|
||||
|
||||
shared_state = State()
|
||||
81
deepseek_vl/serve/app_modules/overwrites.py
Executable file
81
deepseek_vl/serve/app_modules/overwrites.py
Executable file
@@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2023-2024 DeepSeek.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import List, Tuple
|
||||
|
||||
from app_modules.presets import gr
|
||||
from app_modules.utils import convert_asis, convert_mdtext, detect_converted_mark
|
||||
|
||||
|
||||
def compact_text_chunks(self, prompt, text_chunks: List[str]) -> List[str]:
|
||||
logging.debug("Compacting text chunks...🚀🚀🚀")
|
||||
combined_str = [c.strip() for c in text_chunks if c.strip()]
|
||||
combined_str = [f"[{index+1}] {c}" for index, c in enumerate(combined_str)]
|
||||
combined_str = "\n\n".join(combined_str)
|
||||
# resplit based on self.max_chunk_overlap
|
||||
text_splitter = self.get_text_splitter_given_prompt(prompt, 1, padding=1)
|
||||
return text_splitter.split_text(combined_str)
|
||||
|
||||
|
||||
def postprocess(
|
||||
self, y: List[Tuple[str | None, str | None]]
|
||||
) -> List[Tuple[str | None, str | None]]:
|
||||
"""
|
||||
Parameters:
|
||||
y: List of tuples representing the message and response pairs. Each message and response should be a string, which may be in Markdown format.
|
||||
Returns:
|
||||
List of tuples representing the message and response. Each message and response will be a string of HTML.
|
||||
"""
|
||||
if y is None or y == []:
|
||||
return []
|
||||
temp = []
|
||||
for x in y:
|
||||
user, bot = x
|
||||
if not detect_converted_mark(user):
|
||||
user = convert_asis(user)
|
||||
if not detect_converted_mark(bot):
|
||||
bot = convert_mdtext(bot)
|
||||
temp.append((user, bot))
|
||||
return temp
|
||||
|
||||
|
||||
with open("deepseek_vl/serve/assets/custom.js", "r", encoding="utf-8") as f, open(
|
||||
"deepseek_vl/serve/assets/Kelpy-Codos.js", "r", encoding="utf-8"
|
||||
) as f2:
|
||||
customJS = f.read()
|
||||
kelpyCodos = f2.read()
|
||||
|
||||
|
||||
def reload_javascript():
|
||||
print("Reloading javascript...")
|
||||
js = f"<script>{customJS}</script><script>{kelpyCodos}</script>"
|
||||
|
||||
def template_response(*args, **kwargs):
|
||||
res = GradioTemplateResponseOriginal(*args, **kwargs)
|
||||
res.body = res.body.replace(b"</html>", f"{js}</html>".encode("utf8"))
|
||||
res.init_headers()
|
||||
return res
|
||||
|
||||
gr.routes.templates.TemplateResponse = template_response
|
||||
|
||||
|
||||
GradioTemplateResponseOriginal = gr.routes.templates.TemplateResponse
|
||||
96
deepseek_vl/serve/app_modules/presets.py
Executable file
96
deepseek_vl/serve/app_modules/presets.py
Executable file
@@ -0,0 +1,96 @@
|
||||
# Copyright (c) 2023-2024 DeepSeek.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
import gradio as gr
|
||||
|
||||
title = """<h1 align="left" style="min-width:200px; margin-top:0;">Chat with DeepSeek-VL </h1>"""
|
||||
description_top = """"""
|
||||
description = """"""
|
||||
CONCURRENT_COUNT = 10
|
||||
|
||||
|
||||
ALREADY_CONVERTED_MARK = "<!-- ALREADY CONVERTED BY PARSER. -->"
|
||||
|
||||
small_and_beautiful_theme = gr.themes.Soft(
|
||||
primary_hue=gr.themes.Color(
|
||||
c50="#EBFAF2",
|
||||
c100="#CFF3E1",
|
||||
c200="#A8EAC8",
|
||||
c300="#77DEA9",
|
||||
c400="#3FD086",
|
||||
c500="#02C160",
|
||||
c600="#06AE56",
|
||||
c700="#05974E",
|
||||
c800="#057F45",
|
||||
c900="#04673D",
|
||||
c950="#2E5541",
|
||||
name="small_and_beautiful",
|
||||
),
|
||||
secondary_hue=gr.themes.Color(
|
||||
c50="#576b95",
|
||||
c100="#576b95",
|
||||
c200="#576b95",
|
||||
c300="#576b95",
|
||||
c400="#576b95",
|
||||
c500="#576b95",
|
||||
c600="#576b95",
|
||||
c700="#576b95",
|
||||
c800="#576b95",
|
||||
c900="#576b95",
|
||||
c950="#576b95",
|
||||
),
|
||||
neutral_hue=gr.themes.Color(
|
||||
name="gray",
|
||||
c50="#f6f7f8",
|
||||
# c100="#f3f4f6",
|
||||
c100="#F2F2F2",
|
||||
c200="#e5e7eb",
|
||||
c300="#d1d5db",
|
||||
c400="#B2B2B2",
|
||||
c500="#808080",
|
||||
c600="#636363",
|
||||
c700="#515151",
|
||||
c800="#393939",
|
||||
# c900="#272727",
|
||||
c900="#2B2B2B",
|
||||
c950="#171717",
|
||||
),
|
||||
radius_size=gr.themes.sizes.radius_sm,
|
||||
).set(
|
||||
# button_primary_background_fill="*primary_500",
|
||||
button_primary_background_fill_dark="*primary_600",
|
||||
# button_primary_background_fill_hover="*primary_400",
|
||||
# button_primary_border_color="*primary_500",
|
||||
button_primary_border_color_dark="*primary_600",
|
||||
button_primary_text_color="white",
|
||||
button_primary_text_color_dark="white",
|
||||
button_secondary_background_fill="*neutral_100",
|
||||
button_secondary_background_fill_hover="*neutral_50",
|
||||
button_secondary_background_fill_dark="*neutral_900",
|
||||
button_secondary_text_color="*neutral_800",
|
||||
button_secondary_text_color_dark="white",
|
||||
# background_fill_primary="#F7F7F7",
|
||||
# background_fill_primary_dark="#1F1F1F",
|
||||
# block_title_text_color="*primary_500",
|
||||
block_title_background_fill_dark="*primary_900",
|
||||
block_label_background_fill_dark="*primary_900",
|
||||
input_background_fill="#F6F6F6",
|
||||
# chatbot_code_background_color_dark="*neutral_950",
|
||||
)
|
||||
228
deepseek_vl/serve/app_modules/utils.py
Executable file
228
deepseek_vl/serve/app_modules/utils.py
Executable file
@@ -0,0 +1,228 @@
|
||||
# Copyright (c) 2023-2024 DeepSeek.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import mdtex2html
|
||||
from app_modules.presets import ALREADY_CONVERTED_MARK
|
||||
from markdown import markdown
|
||||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import ClassNotFound, get_lexer_by_name, guess_lexer
|
||||
|
||||
logger = logging.getLogger("gradio_logger")
|
||||
|
||||
|
||||
def configure_logger():
|
||||
logger = logging.getLogger("gradio_logger")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
os.makedirs("deepseek_vl/serve/logs", exist_ok=True)
|
||||
file_handler = logging.FileHandler(
|
||||
f"deepseek_vl/serve/logs/{timestr}_gradio_log.log"
|
||||
)
|
||||
console_handler = logging.StreamHandler()
|
||||
|
||||
formatter = logging.Formatter(
|
||||
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
console_handler.setFormatter(formatter)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
console_handler.setLevel(logging.INFO)
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
||||
logger.addHandler(console_handler)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
def strip_stop_words(x, stop_words):
|
||||
for w in stop_words:
|
||||
if w in x:
|
||||
return x[: x.index(w)].strip()
|
||||
return x.strip()
|
||||
|
||||
|
||||
def format_output(history, text, x):
|
||||
updated_history = history + [[text, x]]
|
||||
a = [[y[0], convert_to_markdown(y[1])] for y in updated_history]
|
||||
return a, updated_history
|
||||
|
||||
|
||||
def markdown_to_html_with_syntax_highlight(md_str): # deprecated
|
||||
def replacer(match):
|
||||
lang = match.group(1) or "text"
|
||||
code = match.group(2)
|
||||
|
||||
try:
|
||||
lexer = get_lexer_by_name(lang, stripall=True)
|
||||
except ValueError:
|
||||
lexer = get_lexer_by_name("text", stripall=True)
|
||||
|
||||
formatter = HtmlFormatter()
|
||||
highlighted_code = highlight(code, lexer, formatter)
|
||||
|
||||
return f'<pre><code class="{lang}">{highlighted_code}</code></pre>'
|
||||
|
||||
code_block_pattern = r"```(\w+)?\n([\s\S]+?)\n```"
|
||||
md_str = re.sub(code_block_pattern, replacer, md_str, flags=re.MULTILINE)
|
||||
|
||||
html_str = markdown(md_str)
|
||||
return html_str
|
||||
|
||||
|
||||
def normalize_markdown(md_text: str) -> str: # deprecated
|
||||
lines = md_text.split("\n")
|
||||
normalized_lines = []
|
||||
inside_list = False
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if re.match(r"^(\d+\.|-|\*|\+)\s", line.strip()):
|
||||
if not inside_list and i > 0 and lines[i - 1].strip() != "":
|
||||
normalized_lines.append("")
|
||||
inside_list = True
|
||||
normalized_lines.append(line)
|
||||
elif inside_list and line.strip() == "":
|
||||
if i < len(lines) - 1 and not re.match(
|
||||
r"^(\d+\.|-|\*|\+)\s", lines[i + 1].strip()
|
||||
):
|
||||
normalized_lines.append(line)
|
||||
continue
|
||||
else:
|
||||
inside_list = False
|
||||
normalized_lines.append(line)
|
||||
|
||||
return "\n".join(normalized_lines)
|
||||
|
||||
|
||||
def convert_mdtext(md_text):
|
||||
code_block_pattern = re.compile(r"```(.*?)(?:```|$)", re.DOTALL)
|
||||
inline_code_pattern = re.compile(r"`(.*?)`", re.DOTALL)
|
||||
code_blocks = code_block_pattern.findall(md_text)
|
||||
non_code_parts = code_block_pattern.split(md_text)[::2]
|
||||
|
||||
result = []
|
||||
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
||||
if non_code.strip():
|
||||
non_code = normalize_markdown(non_code)
|
||||
if inline_code_pattern.search(non_code):
|
||||
result.append(markdown(non_code, extensions=["tables"]))
|
||||
else:
|
||||
result.append(mdtex2html.convert(non_code, extensions=["tables"]))
|
||||
if code.strip():
|
||||
code = f"\n```{code}\n\n```"
|
||||
code = markdown_to_html_with_syntax_highlight(code)
|
||||
result.append(code)
|
||||
result = "".join(result)
|
||||
result += ALREADY_CONVERTED_MARK
|
||||
return result
|
||||
|
||||
|
||||
def convert_asis(userinput):
|
||||
return f'<p style="white-space:pre-wrap;">{html.escape(userinput)}</p>{ALREADY_CONVERTED_MARK}'
|
||||
|
||||
|
||||
def is_stop_word_or_prefix(s: str, stop_words: list) -> bool:
|
||||
return any(s.endswith(stop_word) for stop_word in stop_words)
|
||||
|
||||
|
||||
def detect_converted_mark(userinput):
|
||||
return bool(userinput.endswith(ALREADY_CONVERTED_MARK))
|
||||
|
||||
|
||||
def detect_language(code):
|
||||
first_line = "" if code.startswith("\n") else code.strip().split("\n", 1)[0]
|
||||
language = first_line.lower() if first_line else ""
|
||||
code_without_language = code[len(first_line) :].lstrip() if first_line else code
|
||||
return language, code_without_language
|
||||
|
||||
|
||||
def convert_to_markdown(text):
|
||||
text = text.replace("$", "$")
|
||||
text = text.replace("\r\n", "\n")
|
||||
|
||||
def replace_leading_tabs_and_spaces(line):
|
||||
new_line = []
|
||||
|
||||
for char in line:
|
||||
if char == "\t":
|
||||
new_line.append("	")
|
||||
elif char == " ":
|
||||
new_line.append(" ")
|
||||
else:
|
||||
break
|
||||
return "".join(new_line) + line[len(new_line) :]
|
||||
|
||||
markdown_text = ""
|
||||
lines = text.split("\n")
|
||||
in_code_block = False
|
||||
|
||||
for line in lines:
|
||||
if in_code_block is False and line.startswith("```"):
|
||||
in_code_block = True
|
||||
markdown_text += f"{line}\n"
|
||||
elif in_code_block is True and line.startswith("```"):
|
||||
in_code_block = False
|
||||
markdown_text += f"{line}\n"
|
||||
elif in_code_block:
|
||||
markdown_text += f"{line}\n"
|
||||
else:
|
||||
line = replace_leading_tabs_and_spaces(line)
|
||||
line = re.sub(r"^(#)", r"\\\1", line)
|
||||
markdown_text += f"{line} \n"
|
||||
|
||||
return markdown_text
|
||||
|
||||
|
||||
def add_language_tag(text):
|
||||
def detect_language(code_block):
|
||||
try:
|
||||
lexer = guess_lexer(code_block)
|
||||
return lexer.name.lower()
|
||||
except ClassNotFound:
|
||||
return ""
|
||||
|
||||
code_block_pattern = re.compile(r"(```)(\w*\n[^`]+```)", re.MULTILINE)
|
||||
|
||||
def replacement(match):
|
||||
code_block = match.group(2)
|
||||
if match.group(2).startswith("\n"):
|
||||
language = detect_language(code_block)
|
||||
return (
|
||||
f"```{language}{code_block}```" if language else f"```\n{code_block}```"
|
||||
)
|
||||
else:
|
||||
return match.group(1) + code_block + "```"
|
||||
|
||||
text2 = code_block_pattern.sub(replacement, text)
|
||||
return text2
|
||||
|
||||
|
||||
def is_variable_assigned(var_name: str) -> bool:
|
||||
return var_name in locals()
|
||||
Reference in New Issue
Block a user