RAG5_2_ChooseLLM / app_gradio.py
jeongsoo's picture
Add application file
6be0e6a
import os
import gradio as gr
import json
import logging
from app.app import app as flask_app # Flask ์•ฑ ๊ฐ€์ ธ์˜ค๊ธฐ
from flask import json as flask_json
from retrying import retry
# ๋กœ๊ฑฐ ์„ค์ •
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG # INFO์—์„œ DEBUG๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋” ์ƒ์„ธํ•œ ๋กœ๊ทธ ํ™•์ธ
)
logger = logging.getLogger(__name__)
# Flask ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
@retry(tries=5, delay=1, backoff=2)
def init_flask_client():
"""๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰ Flask ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”"""
try:
test_client = flask_app.test_client()
logger.info("Flask ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์„ฑ๊ณต")
return test_client
except Exception as e:
logger.error(f"Flask ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {e}")
raise e
# ์†Œ์Šค ์ •๋ณด ํฌ๋งทํŒ… ํ—ค๋ฃจํผ ํ•จ์ˆ˜
def format_source_info(sources, prefix=""):
"""๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์†Œ์Šค ์ •๋ณด๋ฅผ ํ˜•์‹ํ™”
Args:
sources: ์†Œ์Šค ์ •๋ณด ๋ฆฌ์ŠคํŠธ
prefix: ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ์ ‘๋‘์‚ฌ (๊ธฐ๋ณธ๊ฐ’: "")
Returns:
ํ˜•์‹ํ™”๋œ ์†Œ์Šค ์ •๋ณด ๋ฌธ์ž์—ด
"""
source_info = ""
if sources and len(sources) > 0:
logger.debug(f"{prefix}์†Œ์Šค ์ •๋ณด ํฌ๋งทํŒ…: {len(sources)}๊ฐœ ์†Œ์Šค: {json.dumps(sources, ensure_ascii=False, indent=2)}")
source_list = []
for src in sources:
source_text = src["source"]
# id ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ๊ป˜ ํ‘œ์‹œ
if "id" in src:
logger.debug(f"{prefix}ID ํ•„๋“œ ๋ฐœ๊ฒฌ: {src['id']}")
source_text += f" (ID: {src['id']})"
source_list.append(source_text)
if source_list:
source_info = "\n\n์ฐธ์กฐ ์†Œ์Šค:\n" + "\n".join(source_list)
logger.debug(f"{prefix}์ตœ์ข… ์†Œ์Šค ์ •๋ณด ํ˜•์‹: {source_info}")
return source_info
# Flask ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
flask_client = init_flask_client()
# Gradio ๋ฒ„์ „ ํ™•์ธ ๋ฉ”์‹œ์ง€
logger.info(f"Gradio ๋ฒ„์ „: {gr.__version__}")
# Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
with gr.Blocks(title="RAG ๊ฒ€์ƒ‰ ์ฑ—๋ด‡ with ์Œ์„ฑ์ธ์‹") as demo:
gr.HTML("""
<div style="text-align: center; max-width: 800px; margin: 0 auto;">
<h1>RAG ๊ฒ€์ƒ‰ ์ฑ—๋ด‡ with ์Œ์„ฑ์ธ์‹</h1>
<p>ํ…์ŠคํŠธ ๋˜๋Š” ์Œ์„ฑ์œผ๋กœ ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”.</p>
</div>
""")
with gr.Tab("ํ…์ŠคํŠธ ์ฑ—"):
text_input = gr.Textbox(label="์งˆ๋ฌธ ์ž…๋ ฅ", placeholder="์—ฌ๊ธฐ์— ์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”...")
text_output = gr.Textbox(label="์‘๋‹ต", interactive=False)
text_button = gr.Button("์งˆ๋ฌธ ์ œ์ถœ")
with gr.Tab("์Œ์„ฑ ์ฑ—"):
with gr.Row():
# ๋…น์Œ UI ๊ฐœ์„ : ๋งˆ์ดํฌ๋กœํฐ์œผ๋กœ ์†Œ์Šค ์ง€์ • ๋ฐ ์ŒํŒŒ ํ‘œ์‹œ ํ™œ์„ฑํ™”
audio_input = gr.Audio(
label="์Œ์„ฑ ์ž…๋ ฅ",
type="filepath",
sources=["microphone"],
show_label=True,
waveform_options={"show_controls": True, "normalize": True},
interactive=True
)
audio_transcription = gr.Textbox(label="์ธ์‹๋œ ํ…์ŠคํŠธ", interactive=False)
audio_output = gr.Textbox(label="์‘๋‹ต", interactive=False)
gr.Markdown("""
<div style="text-align: center; margin: 10px 0;">
<p>๋…น์Œ ์ •์ง€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ž๋™์œผ๋กœ ์Œ์„ฑ์ด ์ „์†ก๋ฉ๋‹ˆ๋‹ค.</p>
</div>
""")
with gr.Tab("๋ฌธ์„œ ์—…๋กœ๋“œ"):
doc_input = gr.File(label="๋ฌธ์„œ ์—…๋กœ๋“œ", file_types=[".txt", ".md", ".pdf", ".docx", ".csv"])
doc_output = gr.Textbox(label="์—…๋กœ๋“œ ๊ฒฐ๊ณผ", interactive=False)
doc_button = gr.Button("๋ฌธ์„œ ์—…๋กœ๋“œ")
# ํ…์ŠคํŠธ ์ฑ— ๊ธฐ๋Šฅ
def handle_text_chat(query):
if not query:
return "์งˆ๋ฌธ์„ ์ž…๋ ฅํ•˜์„ธ์š”."
try:
logger.info("ํ…์ŠคํŠธ ์ฑ— ์š”์ฒญ: /api/chat")
response = flask_client.post("/api/chat", json={"query": query})
data = flask_json.loads(response.data)
# ๋””๋ฒ„๊น…์„ ์œ„ํ•œ API ์‘๋‹ต ๋กœ๊ทธ
logger.info(f"API ์‘๋‹ต ๊ตฌ์กฐ: {json.dumps(data, ensure_ascii=False, indent=2)[:500]}...")
if "error" in data:
logger.error(f"ํ…์ŠคํŠธ ์ฑ— ์˜ค๋ฅ˜: {data['error']}")
return data["error"]
# ์†Œ์Šค ์ •๋ณด ์ถ”์ถœ ๋ฐ ํฌ๋งทํŒ…
source_info = ""
if "sources" in data and data["sources"]:
source_info = format_source_info(data["sources"])
# ์‘๋‹ต๊ณผ ์†Œ์Šค ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ๋ฐ˜ํ™˜
return data["answer"] + source_info
except Exception as e:
logger.error(f"ํ…์ŠคํŠธ ์ฑ— ์ฒ˜๋ฆฌ ์‹คํŒจ: {str(e)}")
return f"์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
# ์Œ์„ฑ ์ฑ— ๊ธฐ๋Šฅ
def handle_voice_chat(audio_file):
if not audio_file:
return "", "์Œ์„ฑ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”."
try:
logger.info("์Œ์„ฑ ์ฑ— ์š”์ฒญ: /api/voice")
with open(audio_file, "rb") as f:
# Flask ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ๋Š” files ์ง์ ‘ ์ง€์› ์•ˆ ํ•˜๋ฏ€๋กœ, ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ์ „๋‹ฌ
response = flask_client.post(
"/api/voice",
data={"audio": (f, "audio_file")}
)
data = flask_json.loads(response.data)
# ๋””๋ฒ„๊น…์„ ์œ„ํ•œ API ์‘๋‹ต ๋กœ๊ทธ
logger.info(f"[์Œ์„ฑ์ฑ—] API ์‘๋‹ต ๊ตฌ์กฐ: {json.dumps(data, ensure_ascii=False, indent=2)[:500]}...")
if "error" in data:
logger.error(f"์Œ์„ฑ ์ฑ— ์˜ค๋ฅ˜: {data['error']}")
return "", data["error"]
# ์†Œ์Šค ์ •๋ณด ์ถ”์ถœ ๋ฐ ํฌ๋งทํŒ…
source_info = ""
if "sources" in data and data["sources"]:
source_info = format_source_info(data["sources"], prefix="[์Œ์„ฑ์ฑ—] ")
# ์ธ์‹๋œ ํ…์ŠคํŠธ์™€ ์†Œ์Šค ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ์‘๋‹ต ๋ฐ˜ํ™˜
return data["transcription"], data["answer"] + source_info
except Exception as e:
logger.error(f"์Œ์„ฑ ์ฑ— ์ฒ˜๋ฆฌ ์‹คํŒจ: {str(e)}")
return "", f"์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
# ๋ฌธ์„œ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ
def handle_doc_upload(doc_file):
if not doc_file:
return "๋ฌธ์„œ๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”."
try:
logger.info(f"๋ฌธ์„œ ์—…๋กœ๋“œ ์š”์ฒญ: /api/upload, ํŒŒ์ผ๋ช…: {doc_file.name}")
file_extension = os.path.splitext(doc_file.name)[1].lower()
logger.info(f"ํŒŒ์ผ ํ™•์žฅ์ž: {file_extension}")
with open(doc_file, "rb") as f:
response = flask_client.post(
"/api/upload",
data={"document": (f, doc_file.name)}
)
data = flask_json.loads(response.data)
if "error" in data:
logger.error(f"๋ฌธ์„œ ์—…๋กœ๋“œ ์˜ค๋ฅ˜: {data['error']}")
return data["error"]
return data["message"]
except Exception as e:
logger.error(f"๋ฌธ์„œ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ ์‹คํŒจ: {str(e)}")
return f"์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
# ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์—ฐ๊ฒฐ
text_button.click(
fn=handle_text_chat,
inputs=text_input,
outputs=text_output
)
# ์Œ์„ฑ ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์ž๋™์œผ๋กœ ์ „์†ก
audio_input.change(
fn=handle_voice_chat,
inputs=audio_input,
outputs=[audio_transcription, audio_output]
)
doc_button.click(
fn=handle_doc_upload,
inputs=doc_input,
outputs=doc_output
)
if __name__ == "__main__":
demo.launch(server_port=7860)