필요한 html부분만 추출하면 이런 식으로 되어 있는 것인데, 앞에 link된 style sheet가 너무 많아서 !important도 인라인 style도 제대로 작동이 안되어 코드부분에서 넘쳐나 짤려요. 그래서 link된 style sheet를 모두 제거하고 새로 만드는 것이 앞서 대화의 결론이었죠.

<!DOCTYPE html><html lang="ko" style="
		margin: 0;
		padding: 0;
		min-height: 100%;
		display: flex;
		flex-direction: column;
	"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="cid:css-cd14aa32-e98b-48c8-863c-434d0edd2457@mhtml.blink" /><link rel="stylesheet" type="text/css" href="cid:css-b98d4f89-5cc5-4c91-8b7d-7df7ce3aae66@mhtml.blink" />
		
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

		

		<meta property="og:url" content="https://gradio.app/">
		<meta property="og:type" content="website">
		<meta property="og:image" content="">
		<meta property="og:title" content="Gradio">
		<meta property="og:description" content="">
		<meta name="twitter:card" content="summary_large_image">
		<meta name="twitter:creator" content="@teamGradio">
		<meta name="twitter:title" content="Gradio">
		<meta name="twitter:description" content="">
		<meta name="twitter:image" content="">

		

		

		<link rel="preconnect" href="https://fonts.googleapis.com/">
		<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin="anonymous">
		
		
		<link rel="stylesheet" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/index-DuXXhepF.css">
	<link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-D21IHG0c.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-BEyjvDG_.css"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/index-DuXXhepF.css"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/theme.css?v=0d58f89b4c2aef0d338b84979b9ffb76d102d25035087d6771f50989d2612182"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Blocks-Dw_9NR1K.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/index-D5ROCp7B.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Button-uOcat6Z0.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Button-CTZL5Nos.css"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Blocks-yLdzXwzS.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&amp;display=swap"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&amp;display=swap"><title>Gradio</title><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-QFLfeHqR.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Code-DGNrTu_I.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/BlockLabel-BXXlQleC.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-BicZ6NTU.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-3Ud1cpPD.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-CptIZeFZ.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-DDqzdfNS.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/index-CnqicUFC.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/ShareButton-OlciWAJu.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Image-DG8jX6JY.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/file-url-BIHPd7vS.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Image-B8dFOee4.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index.svelte_svelte_type_style_lang-D199jKmZ.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Check-CZUQOzJl.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Copy-B6RcHnoK.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Example.svelte_svelte_type_style_lang-qahm4PWk.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/prism-python-Bw3EYPE-.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Example-BOThR97Y.css"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-Xv8ZQETB.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/DownloadLink-BgAM71ly.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-D9RVvV4H.css"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/ImagePreview-DlqNSi_R.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-q5DoCKkE.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Textbox-CF9-08gf.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/BlockTitle-COFLSASJ.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Info-CMIMfBX8.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Textbox-D8IAzrZj.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Example-C7XUkkid.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Example-Cj3ii62O.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-CwNLzWTz.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-C43SQM_O.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-DwSO5GE1.js"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-DDCF2BFd.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-B0JJ6p9c.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-BpBoLkq9.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-C5NYahSl.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-CWcDktiU.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-CJeiS5ET.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-Ix1HTuFr.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-BUublr_b.css"><link rel="modulepreload" as="script" crossorigin="" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-CzlZdV35.js"><link rel="stylesheet" href="https://yuntian-deng-chatgpt4.hf.space/assets/Index-BTCbxfal.css"></head>

	<body style="width: 100%; margin: 0px; padding: 0px; display: flex; flex-direction: column; flex-grow: 1; --chatbot-body-text-size: 16px;">
		<gradio-app control_page_title="true" embed="false" eager="true" style="display: flex; flex-direction: column; flex-grow: 1; background: var(--body-background-fill);">
		<div class="gradio-container gradio-container-4-44-0 svelte-wpkpf6 app" data-iframe-height="" style="min-height: initial; flex-grow: 1;"><div class="main svelte-wpkpf6">  <div class="wrap svelte-1rjryqp" style="min-height: 100%;"><div class="contain svelte-1rjryqp" style="flex-grow: 1;"><div id="component-0" class="svelte-vt1mxs gap" style="min-width: min(0px, 100%);"> <div id="component-1" class="block svelte-12cmxck padded hide-container" style="border-style: solid; overflow: visible; min-width: min(0px, 100%); border-width: var(--block-border-width);"> <div class="wrap center full svelte-au1olv hide" style="position: absolute; padding: 0px;"></div> <div class="svelte-uqf9ro"><div class="prose  svelte-1ybaih5"><h1 align="center">GPT-4o: Research Preview (Short-Term Availability)</h1></div></div></div><div id="component-2" class="block svelte-12cmxck padded hide-container" style="border-style: solid; overflow: visible; min-width: min(0px, 100%); border-width: var(--block-border-width);"> <div class="wrap center full svelte-au1olv hide" style="position: absolute; padding: 0px;"></div> <div class="svelte-uqf9ro"><div class="prose  svelte-1ybaih5"><h3 align="center" style="color: red;">If this app doesn't respond, consider trying our other GPT-4O app:<br><a href="https://huggingface.co/spaces/yuntian-deng/ChatGPT4Turbo" target="_blank">https://huggingface.co/spaces/yuntian-deng/ChatGPT4Turbo</a></h3></div></div></div><div id="col_container" class="svelte-vt1mxs gap" style="flex-grow: 1; min-width: min(320px, 100%);"> <div id="chatbot" class="block  svelte-12cmxck" style="height: 400px; border-style: solid; overflow: hidden; min-width: min(160px, 100%); border-width: var(--block-border-width);"><div class="wrap default minimal svelte-au1olv translucent hide" style="position: absolute; padding: 0px;"></div> <div class="wrapper svelte-nab2ao"><label for="" data-testid="block-label" class="svelte-1b6s6s float"><span class="svelte-1b6s6s"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--carbon" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path fill="currentColor" d="M17.74 30L16 29l4-7h6a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h9v2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4h20a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4h-4.84Z"></path><path fill="currentColor" d="M8 10h16v2H8zm0 6h10v2H8z"></path></svg></span> Chatbot</label> <div class="share-button svelte-1e1jlin"><button aria-label="Share" aria-haspopup="false" title="Share" class="svelte-rk35yg padded" style="color: var(--block-label-text-color); --bg-color: var(--background-fill-primary); margin-left: 0px;"> <div class="svelte-rk35yg small"><svg id="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M23,20a5,5,0,0,0-3.89,1.89L11.8,17.32a4.46,4.46,0,0,0,0-2.64l7.31-4.57A5,5,0,1,0,18,7a4.79,4.79,0,0,0,.2,1.32l-7.31,4.57a5,5,0,1,0,0,6.22l7.31,4.57A4.79,4.79,0,0,0,18,25a5,5,0,1,0,5-5ZM23,4a3,3,0,1,1-3,3A3,3,0,0,1,23,4ZM7,19a3,3,0,1,1,3-3A3,3,0,0,1,7,19Zm16,9a3,3,0,1,1,3-3A3,3,0,0,1,23,28Z" fill="currentColor"></path></svg></div></button></div>  <div class="bubble-wrap svelte-1e1jlin" role="log" aria-label="chatbot conversation" aria-live="polite"><div class="message-wrap svelte-1e1jlin"> <div class="message-row bubble user-row svelte-1e1jlin"> <div class="flex-wrap user svelte-1e1jlin"><div class="message user svelte-1e1jlin panel-full-width" style="text-align: right;"><button data-testid="user" dir="ltr" aria-label="user's message: 제 허깅페이스 스페이스에 다음과 같은 파일로 App을 만들어봤는데, 여기에 AI와의 대화내용을 html 파일로 다운로드해주는 기능을 추가할 수 있을까요?(오늘 Gradio가 5.4.0에서 5.5.0으로 업그레이드 됐는데 그래서 그런지, 다른 이유에서인지, 그 AI의 글이 초반부터 이상하게 나오며 짤리기도 해서 여기에 물어보는 거예요.)

그리고 여기에서의 대화도 Edge 브라우저의 다운로드 기능으로 저장해서 보면, 뒷부분이 플로팅 고정창 때문인지 출력이 안되므로, mht 편집기로 md 글이나 html 글을 따로 저장해야 하므로 불편해서요. 허깅페이스 스페이스의 문제...

import gradio as gr
from huggingface_hub import InferenceClient
import re

# Initialize the InferenceClient
client = InferenceClient(&quot;mistralai/Mistral-Nemo-Instruct-2407&quot;)

def remove_unwanted_emojis(text: str) -> str:
    unwanted_emoji_pattern = re.compile(
        &quot;[\U0001F600-\U0001F64F&quot;
        &quot;\U0001F400-\U0001F43E&quot;
        &quot;\U0001F47B&quot;
        &quot;\U0001F920&quot;
        &quot;]+&quot;,
        flags=re.UNICODE,
    )
    return unwanted_emoji_pattern.sub(r'', text)

def explain_ai_concept(concept: str, system_message: str, max_tokens: int, temperature: float, top_p: float):
    messages = [{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: system_message}]
    messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: f&quot;Explain '{concept}' in simple terms.&quot;})

    explanation = &quot;&quot;
    try:
        for msg in client.chat_completion(
            messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
        ):
            explanation += msg.choices[0].delta.content
    except Exception as e:
        return f&quot;Error: {str(e)}&quot;

    return remove_unwanted_emojis(explanation)

def respond(user_message: str, chat_history: list[tuple[str, str]], system_message: str, max_tokens: int, temperature: float, top_p: float):
    chat_messages = [{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: system_message}]

    for user, assistant in chat_history:
        if user:
            chat_messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user})
        if assistant:
            chat_messages.append({&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: assistant})

    chat_messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_message})

    response = &quot;&quot;
    try:
        for msg in client.chat_completion(
            chat_messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
        ):
            response += msg.choices[0].delta.content
            response = remove_unwanted_emojis(response)
            yield response
    except Exception as e:
        yield f&quot;Error: {str(e)}&quot;

demo = gr.ChatInterface(
    fn=respond,
    additional_inputs=[
        gr.Textbox(value=&quot;You are a friendly Chatbot that can also explain AI concepts.&quot;, label=&quot;System message&quot;),
        gr.Slider(minimum=1, maximum=2048, value=512, step=1, label=&quot;Max new tokens&quot;),
        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label=&quot;Temperature&quot;),
        gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label=&quot;Top-p (Nucleus Sampling)&quot;),
    ],
)

if __name__ == &quot;__main__&quot;:
    demo.launch()" class="svelte-1e1jlin" style="user-select: text; cursor: default; text-align: left;"><span class="md svelte-8tpqd2 chatbot prose"><p>제 허깅페이스 스페이스에 다음과 같은 파일로 App을 만들어봤는데, 여기에 AI와의 대화내용을 html 파일로 다운로드해주는 기능을 추가할 수 있을까요?(오늘 Gradio가 5.4.0에서 5.5.0으로 업그레이드 됐는데 그래서 그런지, 다른 이유에서인지, 그 AI의 글이 초반부터 이상하게 나오며 짤리기도 해서 여기에 물어보는 거예요.)</p>
<p>그리고 여기에서의 대화도 Edge 브라우저의 다운로드 기능으로 저장해서 보면, 뒷부분이 플로팅 고정창 때문인지 출력이 안되므로, mht 편집기로 md 글이나 html 글을 따로 저장해야 하므로 불편해서요. 허깅페이스 스페이스의 문제...</p>
<p>import gradio as gr<br>from huggingface_hub import InferenceClient<br>import re</p>
<h1>Initialize the InferenceClient</h1>
<p>client = InferenceClient("mistralai/Mistral-Nemo-Instruct-2407")</p>
<p>def remove_unwanted_emojis(text: str) -&gt; str:<br>    unwanted_emoji_pattern = re.compile(<br>        "[\U0001F600-\U0001F64F"<br>        "\U0001F400-\U0001F43E"<br>        "\U0001F47B"<br>        "\U0001F920"<br>        "]+",<br>        flags=re.UNICODE,<br>    )<br>    return unwanted_emoji_pattern.sub(r'', text)</p>
<p>def explain_ai_concept(concept: str, system_message: str, max_tokens: int, temperature: float, top_p: float):<br>    messages = [{"role": "system", "content": system_message}]<br>    messages.append({"role": "user", "content": f"Explain '{concept}' in simple terms."})</p>
<div class="code_wrap"><button class="copy_code_button" title="copy">
<span class="copy-text"><svg viewBox="0 0 32 32" height="100%" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M28 10v18H10V10h18m0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2Z" fill="currentColor"></path><path d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z" fill="currentColor"></path></svg></span>
<span class="check"><svg stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="currentColor" fill="none" viewBox="0 0 24 24" height="100%" width="100%" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>
</button><pre><code>explanation = ""
try:
    for msg in client.chat_completion(
        messages,
        max_tokens=max_tokens,
        stream=True,
        temperature=temperature,
        top_p=top_p,
    ):
        explanation += msg.choices[0].delta.content
except Exception as e:
    return f"Error: {str(e)}"

return remove_unwanted_emojis(explanation)
</code></pre></div>
<p>def respond(user_message: str, chat_history: list[tuple[str, str]], system_message: str, max_tokens: int, temperature: float, top_p: float):<br>    chat_messages = [{"role": "system", "content": system_message}]</p>
<div class="code_wrap"><button class="copy_code_button" title="copy">
<span class="copy-text"><svg viewBox="0 0 32 32" height="100%" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M28 10v18H10V10h18m0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2Z" fill="currentColor"></path><path d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z" fill="currentColor"></path></svg></span>
<span class="check"><svg stroke-linejoin="round" stroke-linecap="round" stroke-width="3" stroke="currentColor" fill="none" viewBox="0 0 24 24" height="100%" width="100%" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>
</button><pre><code>for user, assistant in chat_history:
    if user:
        chat_messages.append({"role": "user", "content": user})
    if assistant:
        chat_messages.append({"role": "assistant", "content": assistant})

chat_messages.append({"role": "user", "content": user_message})

response = ""
try:
    for msg in client.chat_completion(
        chat_messages,
        max_tokens=max_tokens,
        stream=True,
        temperature=temperature,
        top_p=top_p,
    ):
        response += msg.choices[0].delta.content
        response = remove_unwanted_emojis(response)
        yield response
except Exception as e:
    yield f"Error: {str(e)}"
</code></pre></div>
<p>demo = gr.ChatInterface(<br>    fn=respond,<br>    additional_inputs=[<br>        gr.Textbox(value="You are a friendly Chatbot that can also explain AI concepts.", label="System message"),<br>        gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),<br>        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),<br>        gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p (Nucleus Sampling)"),<br>    ],<br>)</p>
<p>if <strong>name</strong> == "<strong>main</strong>":<br>    demo.launch()</p>
</span></button> </div></div></div>  <div class="message-row bubble bot-row svelte-1e1jlin"> <div class="flex-wrap bot svelte-1e1jlin"><div class="message bot svelte-1e1jlin panel-full-width" style="text-align: right;"><button data-testid="bot" dir="ltr" aria-label="bot's message: 기존의 코드를 사용하여 AI와의 대화 내용을 HTML 파일로 다운로드할 수 있는 기능을 추가할 수 있습니다. 이를 위해서는 `gr.File` 컴포넌트를 사용하여 파일 다운로드를 지원하는 방법을 적용할 수 있습니다. 다만 이 기능을 구현하려면 대화 내용 전체를 관리하고, 이를 HTML 형식으로 변환하는 추가적인 작업이 필요합니다. 아래는 이를 구현한 예제 코드입니다.

```python
import gradio as gr
from huggingface_hub import InferenceClient
import re

# Initialize the InferenceClient
client = InferenceClient(&quot;mistralai/Mistral-Nemo-Instruct-2407&quot;)

def remove_unwanted_emojis(text: str) -> str:
    unwanted_emoji_pattern = re.compile(
        &quot;[\U0001F600-\U0001F64F&quot;
        &quot;\U0001F400-\U0001F43E&quot;
        &quot;\U0001F47B&quot;
        &quot;\U0001F920&quot; &quot;]+&quot;,
        flags=re.UNICODE,
    )
    return unwanted_emoji_pattern.sub(r'', text)

def explain_ai_concept(concept: str, system_message: str, max_tokens: int, temperature: float, top_p: float):
    messages = [{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: system_message}]
    messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: f&quot;Explain '{concept}' in simple terms.&quot;})

    explanation = &quot;&quot;
    try:
        for msg in client.chat_completion(
            messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
        ):
            explanation += msg.choices[0].delta.content
    except Exception as e:
        return f&quot;Error: {str(e)}&quot;

    return remove_unwanted_emojis(explanation)

def respond(user_message: str, chat_history: list[tuple[str, str]], system_message: str, max_tokens: int, temperature: float, top_p: float):
    chat_messages = [{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: system_message}]
    
    for user, assistant in chat_history:
        if user:
            chat_messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user})
        if assistant:
            chat_messages.append({&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: assistant})

    chat_messages.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_message})

    response = &quot;&quot;
    try:
        for msg in client.chat_completion(
            chat_messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
        ):
            response += msg.choices[0].delta.content
            response = remove_unwanted_emojis(response)
            yield &quot;&quot;, response  # Return empty file download element and response
    except Exception as e:
        yield &quot;&quot;, f&quot;Error: {str(e)}&quot;

def download_html(chat_history: list[tuple[str, str]]):
    html_content = &quot;<html><body><h1>Chat History</h1><ul>&quot;
    for user_message, ai_response in chat_history:
        html_content += f&quot;<li><strong>User:</strong> {user_message}</li>&quot;
        html_content += f&quot;<li><strong>AI:</strong> {ai_response}</li>&quot;
    html_content += &quot;</ul></body></html>



==========

이런 거로 추출했던 거고요.

간단하게는 이 app.py에서 될 수 있으면 좋겠네요. 혼자만 쓰는 App이고 혼자만 읽고 말 거라서 간단한 기능이나 구조만 있어도 좋을 것 같고요.

==========

import gradio as gr
import re
import tempfile

def convert_mht_to_html(mht_content):
    # 두 번째 HTML 문서 시작 부분인 '<!DOCTYPE html><html lang="ko">'를 기준으로 추출
    match = re.search(r'<!DOCTYPE html><html lang="ko".*?</html>', mht_content, re.DOTALL)
    # match = re.search(r'<!DOCTYPE html>.*?</html>', mht_content, re.DOTALL)
    
    if match:
        # 두 번째 HTML 부분만 추출
        html_content = match.group(0)
        
        # 임시 파일 생성
        with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_file:
            temp_file.write(html_content.encode('utf-8'))
            temp_file_path = temp_file.name
        
        # 임시 파일의 경로 반환
        return temp_file_path
    else:
        # HTML 문서가 없으면 에러 메시지 반환
        return ("No valid HTML content found in the MHT file.", None)

with gr.Blocks() as block:
    gr.Markdown("<h1><center>MHT to HTML Converter</center></h1>")
    
    mht_input = gr.Textbox(label="Paste your MHT content here", lines=20, placeholder="MHT 파일의 내용을 여기에 붙여넣기")
    convert_button = gr.Button("Convert to HTML")
    download_output = gr.File(label="Download HTML File")
    
    convert_button.click(fn=convert_mht_to_html, inputs=mht_input, outputs=download_output)
    
block.launch()