|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os, json, time, requests, gradio as gr |
|
|
from typing import List, Tuple, Dict, Any |
|
|
from datetime import datetime, timedelta |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
API_BASE = os.getenv("HF_ENDPOINT_URL") or "https://router.huggingface.co/v1" |
|
|
MODEL_QA = os.getenv("HF_MODEL_ID") or "Qwen/Qwen2.5-7B-Instruct" |
|
|
HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACEHUB_API_TOKEN") |
|
|
TIMEOUT_S = 60 |
|
|
|
|
|
|
|
|
OPEN_DAYS = [0, 1, 2, 3, 4, 5] |
|
|
OPEN_TIMES = ["15:00", "17:00", "19:00"] |
|
|
SLOT_DAYS_AHEAD = 14 |
|
|
RESV_FILE = "reservations.json" |
|
|
KWDAYS = ["์", "ํ", "์", "๋ชฉ", "๊ธ", "ํ ", "์ผ"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hf_chat(model: str, messages: list, temperature: float = 0.2, max_tokens: int = 256, |
|
|
timeout: int = TIMEOUT_S, retries: int = 3) -> str: |
|
|
if not HF_TOKEN: |
|
|
return "โ ๏ธ HF_TOKEN์ด ์์ต๋๋ค. ํ๊ฒฝ๋ณ์์ HF_TOKEN์ ์ค์ ํ์ธ์." |
|
|
headers = {"Authorization": f"Bearer {HF_TOKEN}", "Content-Type": "application/json"} |
|
|
url = f"{API_BASE}/chat/completions" |
|
|
payload = { |
|
|
"model": model, |
|
|
"messages": messages, |
|
|
"temperature": float(temperature), |
|
|
"max_tokens": int(max_tokens), |
|
|
} |
|
|
|
|
|
backoff = 1.5 |
|
|
for attempt in range(retries): |
|
|
try: |
|
|
r = requests.post(url, headers=headers, data=json.dumps(payload), timeout=timeout) |
|
|
if r.status_code in (429, 500, 502, 503, 504, 529): |
|
|
time.sleep(backoff ** (attempt + 1)) |
|
|
continue |
|
|
if not r.ok: |
|
|
return f"โ HF Router ์ค๋ฅ({r.status_code}): {r.text[:600]}" |
|
|
data = r.json() |
|
|
return (data["choices"][0]["message"]["content"] or "").strip() |
|
|
except Exception: |
|
|
time.sleep(backoff ** (attempt + 1)) |
|
|
return "โ ์์ฒญ ์คํจ(๋คํธ์ํฌ/ํ์์์). ์ ์ ํ ๋ค์ ์๋ํ์ธ์." |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_reservations() -> List[Dict[str, Any]]: |
|
|
if not os.path.exists(RESV_FILE): |
|
|
return [] |
|
|
try: |
|
|
with open(RESV_FILE, "r", encoding="utf-8") as f: |
|
|
return json.load(f) |
|
|
except Exception: |
|
|
return [] |
|
|
|
|
|
def save_reservations(resv: List[Dict[str, Any]]) -> None: |
|
|
tmp = RESV_FILE + ".tmp" |
|
|
with open(tmp, "w", encoding="utf-8") as f: |
|
|
json.dump(resv, f, ensure_ascii=False, indent=2) |
|
|
os.replace(tmp, RESV_FILE) |
|
|
|
|
|
def make_slots(start: datetime | None = None, days: int = SLOT_DAYS_AHEAD) -> List[str]: |
|
|
now = datetime.now() |
|
|
base = start or now |
|
|
out = [] |
|
|
for i in range(days + 1): |
|
|
d = base + timedelta(days=i) |
|
|
if d.weekday() in OPEN_DAYS: |
|
|
for t in OPEN_TIMES: |
|
|
label = f"{d.strftime('%Y-%m-%d')} {t} ({KWDAYS[d.weekday()]})" |
|
|
out.append(label) |
|
|
return out |
|
|
|
|
|
def booked_slots() -> set: |
|
|
return {item["slot"] for item in load_reservations()} |
|
|
|
|
|
def available_slots() -> List[str]: |
|
|
bset = booked_slots() |
|
|
return [s for s in make_slots() if s not in bset] |
|
|
|
|
|
def reservation_rows(resv: List[Dict[str, Any]]) -> List[List[str]]: |
|
|
rows = [] |
|
|
for r in sorted(resv, key=lambda x: x.get("slot", "")): |
|
|
rows.append([ |
|
|
r.get("slot", ""), |
|
|
r.get("name", ""), |
|
|
r.get("grade", ""), |
|
|
r.get("focus", ""), |
|
|
r.get("level", ""), |
|
|
r.get("contact", ""), |
|
|
r.get("notes", ""), |
|
|
r.get("created_at", ""), |
|
|
]) |
|
|
return rows |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def recommend_plan(grade: str, focuses: List[str], level: str, goals: str, |
|
|
weeks: int, hours_per_week: int, constraints: str, |
|
|
temperature: float, max_tokens: int) -> str: |
|
|
focus_str = ", ".join(focuses) if focuses else "์ผ๋ฐ" |
|
|
user_profile = ( |
|
|
f"- ํ๋
: {grade}\n- ๊ด์ฌ ๋ถ์ผ: {focus_str}\n- ์์ค: {level}\n" |
|
|
f"- ๋ชฉํ: {goals or '๋ฏธ์ '}\n- ๊ธฐ๊ฐ: {weeks}์ฃผ\n- ์ฃผ๋น ์๊ฐ: {hours_per_week}์๊ฐ\n" |
|
|
f"- ์ ์ฝ: {constraints or '์์'}" |
|
|
) |
|
|
system = ( |
|
|
"๋น์ ์ ํ์ ์๋ด/์ปค๋ฆฌํ๋ผ ์ค๊ณ ์ ๋ฌธ๊ฐ์
๋๋ค. " |
|
|
"ํ์๊ณผ ํ๋ถ๋ชจ๊ฐ ๋ฐ๋ก ์คํ ๊ฐ๋ฅํ ๊ณํ์ ์ ์ํ์ธ์. " |
|
|
"ํญ์ ์ฃผ์ฐจ๋ณ(Week 1~N) ๋ก๋๋งต, ๊ณผ์ /์ฑ๊ณผ๋ฌผ, ์ถ์ฒ ๊ต์ฌ/๋๊ตฌ, ํ๊ฐ ํฌ์ธํธ๋ฅผ ์งง๊ณ ๋ช
ํํ๊ฒ." |
|
|
) |
|
|
user_prompt = ( |
|
|
"์๋ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์ต์ ์ ํ์ต ์ถ์ฒ์์ ๋ง๋ค์ด ์ฃผ์ธ์.\n\n" |
|
|
+ user_profile + |
|
|
"\n\nํ์:\n" |
|
|
"1) ํ ์ค ์์ฝ\n" |
|
|
"2) ์ฃผ์ฐจ๋ณ ๊ณํ(์ฃผ์ฐจ/ํ์ต๋ชฉํ/ํ๋/๊ณผ์ )\n" |
|
|
"3) ์ถ์ฒ ๊ต์ฌ/ํด(๊ฐ๋จ ๋งํฌ๋ช
๋ง, ์ค์ URL ์๋ต)\n" |
|
|
"4) ์์ ๊ฒฐ๊ณผ๋ฌผ(ํฌํธํด๋ฆฌ์ค/๋ํ/์๊ฒฉ์ฆ)\n" |
|
|
"5) ๋ค์ ์์ฝ ์ ์(์ฒดํ ์์
1ํ + ์ ๊ท ๊ณผ์ )\n" |
|
|
"๋ฌธ์ฅ ์งง๊ฒ. ๋ถํ์ํ ์์์ด ๊ธ์ง." |
|
|
) |
|
|
msgs = [ |
|
|
{"role": "system", "content": system}, |
|
|
{"role": "user", "content": user_prompt}, |
|
|
] |
|
|
out = hf_chat(MODEL_QA, msgs, temperature=temperature, max_tokens=max_tokens) |
|
|
if out.startswith("โ") or out.startswith("โ ๏ธ"): |
|
|
rec = f"""[๊ฐ๋จ ์ถ์ฒ์ โ Fallback] |
|
|
ํ ์ค ์์ฝ: {grade} ๋์ {focus_str} {level} ๊ณผ์ , {weeks}์ฃผ, ์ฃผ {hours_per_week}์๊ฐ. |
|
|
|
|
|
์ฃผ์ฐจ๋ณ: |
|
|
- Week 1: ํ๊ฒฝ ์ค์ /๊ธฐ์ด ๋ฌธ๋ฒ โ ๊ณผ์ : ์ค์น/HelloWorld/๊ฐ๋จ ๋ฌธ์ 3๊ฐ |
|
|
- Week 2: ํต์ฌ ๊ฐ๋
์ตํ๊ธฐ โ ๊ณผ์ : {('์ผ์ ์ ์ด ์ค์ต' if '์๋์ด๋
ธ' in focus_str else '๋ฏธ๋ ํ๋ก์ ํธ 1')} |
|
|
- Week 3: ์ฃผ์ ์ฌํ โ ๊ณผ์ : ๋ฏธ๋ ํ๋ก์ ํธ 2 |
|
|
- Week 4: ์ข
ํฉ ํ๋ก์ ํธ โ ๊ณผ์ : ๋ฐํ ์๋ฃ/ํฌํธํด๋ฆฌ์ค ์ ๋ฆฌ |
|
|
|
|
|
์ถ์ฒ ๋๊ตฌ: VSCode, GitHub(์ฝ๋/๋ณด๊ณ ์), {('Arduino IDE' if '์๋์ด๋
ธ' in focus_str else 'Notion')} |
|
|
์์ ๊ฒฐ๊ณผ๋ฌผ: ํ๋ก์ ํธ 1~2๊ฐ, ํฌํธํด๋ฆฌ์ค ํ์ด์ง |
|
|
๋ค์ ์์ฝ ์ ์: ์ฒดํ 1ํ(60๋ถ) ํ ์ฃผ {hours_per_week}์๊ฐ ์ ๊ท๋ฐ ๋ฑ๋ก |
|
|
""" |
|
|
return rec.strip() |
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def history_to_messages(history: List[Tuple[str, str]], user_text: str) -> list: |
|
|
msgs = [ |
|
|
{"role": "system", |
|
|
"content": ( |
|
|
"๋น์ ์ ๊ฐ๊ฒฐํ๊ณ ์ ํํ ํ๊ตญ์ด ์กฐ์์
๋๋ค. ๋ถํ์ํ ์์์ด ์์ด ํต์ฌ๋ง ๋ตํ์ธ์. " |
|
|
"ํ์(์ด/์ค/๊ณ )๊ณผ ํ๋ถ๋ชจ๊ฐ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๋จ๊ณ๋ณ๋ก ์ค๋ช
ํ๊ณ , ํ์์ ์งง์ ์์๋ฅผ ๋ค์ด์ฃผ์ธ์." |
|
|
)}, |
|
|
] |
|
|
for u, a in history: |
|
|
if u: |
|
|
msgs.append({"role": "user", "content": u}) |
|
|
if a: |
|
|
msgs.append({"role": "assistant", "content": a}) |
|
|
if user_text: |
|
|
msgs.append({"role": "user", "content": user_text}) |
|
|
return msgs |
|
|
|
|
|
def chat_qa(user_input: str, history: List[Tuple[str, str]], temperature: float, max_new_tokens: int): |
|
|
user_input = (user_input or "").strip() |
|
|
if not user_input: |
|
|
return history, "", history |
|
|
msgs = history_to_messages(history, user_input) |
|
|
answer = hf_chat(MODEL_QA, msgs, temperature=temperature, max_tokens=max_new_tokens) |
|
|
history = history + [(user_input, answer)] |
|
|
return history, "", history |
|
|
|
|
|
def reset_chat(): |
|
|
return [], [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def refresh_slots_update(): |
|
|
return gr.update(choices=available_slots(), value=None) |
|
|
|
|
|
def submit_booking(name: str, grade: str, focuses: List[str], level: str, |
|
|
contact: str, slot: str, notes: str): |
|
|
name = (name or "").strip() |
|
|
contact = (contact or "").strip() |
|
|
slot = (slot or "").strip() |
|
|
focus_str = ", ".join(focuses) if focuses else "์ผ๋ฐ" |
|
|
|
|
|
if not name: |
|
|
return "โ ๏ธ ์ด๋ฆ์ ์
๋ ฅํ์ธ์.", reservation_rows(load_reservations()), refresh_slots_update() |
|
|
if not contact: |
|
|
return "โ ๏ธ ์ฐ๋ฝ์ฒ(์ ํ/์นด์นด์ค/์ด๋ฉ์ผ) ์
๋ ฅํ์ธ์.", reservation_rows(load_reservations()), refresh_slots_update() |
|
|
if not slot: |
|
|
return "โ ๏ธ ์์ฝ ์ฌ๋กฏ์ ์ ํํ์ธ์.", reservation_rows(load_reservations()), refresh_slots_update() |
|
|
if slot in booked_slots(): |
|
|
return "โ ์ด๋ฏธ ์์ฝ๋ ์ฌ๋กฏ์
๋๋ค. ๋ค๋ฅธ ์๊ฐ์ ์ ํํ์ธ์.", reservation_rows(load_reservations()), refresh_slots_update() |
|
|
|
|
|
resv = load_reservations() |
|
|
item = { |
|
|
"name": name, |
|
|
"grade": grade, |
|
|
"focus": focus_str, |
|
|
"level": level, |
|
|
"contact": contact, |
|
|
"slot": slot, |
|
|
"notes": (notes or "").strip(), |
|
|
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M"), |
|
|
} |
|
|
resv.append(item) |
|
|
save_reservations(resv) |
|
|
return (f"โ
์์ฝ ์๋ฃ: {slot} ยท {name} ({grade}, {focus_str}/{level}) โ ๋ด๋น์๊ฐ ๊ณง ์ฐ๋ฝ๋๋ฆฝ๋๋ค.", |
|
|
reservation_rows(resv), |
|
|
refresh_slots_update()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def diagnose() -> str: |
|
|
if not HF_TOKEN: |
|
|
return "HF_TOKEN ์์" |
|
|
try: |
|
|
r = requests.get(f"{API_BASE}/models", headers={"Authorization": f"Bearer {HF_TOKEN}"}, timeout=20) |
|
|
head = f"GET /models -> {r.status_code}" |
|
|
body = r.text[:1000] |
|
|
return f"{head}\n{body}" |
|
|
except Exception as e: |
|
|
return f"์์ฒญ ์คํจ: {e}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CSS = """ |
|
|
:root { |
|
|
--primary: #0064ff; |
|
|
--bg: #ffffff; |
|
|
--card: #ffffff; |
|
|
--text: #0b1020; |
|
|
--muted: #6b7280; |
|
|
--stroke: #e6ecf5; |
|
|
--radius: 16px; |
|
|
} |
|
|
.gradio-container { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", sans-serif; background: var(--bg); } |
|
|
.toss-header { |
|
|
padding: 28px 24px; border-radius: 24px; |
|
|
background: linear-gradient(135deg, #eaf2ff 0%, #f6faff 100%); |
|
|
border: 1px solid #e5efff; margin-bottom: 12px; |
|
|
} |
|
|
.toss-title { font-size: 28px; font-weight: 800; color: var(--text); letter-spacing: -0.02em; margin: 0; } |
|
|
.toss-sub { color: var(--muted); margin-top: 6px; font-size: 14px; } |
|
|
.toss-card { |
|
|
background: var(--card); border: 1px solid var(--stroke); |
|
|
border-radius: var(--radius); box-shadow: 0 8px 24px rgba(15,23,42,0.06); padding: 18px; |
|
|
} |
|
|
.toss-primary { background: var(--primary) !important; color: white !important; border-radius: 12px !important; font-weight: 700 !important; } |
|
|
.toss-input textarea, .toss-input input, .toss-input select { border-radius: 14px !important; } |
|
|
.toss-note { color: var(--muted); font-size: 12px; } |
|
|
footer { display: none !important; } |
|
|
label { font-weight: 700 !important; } |
|
|
.gr-chatbot { border-radius: var(--radius); border: 1px solid var(--stroke); } |
|
|
.gr-chatbot .message { border-radius: 14px !important; } |
|
|
.gr-chatbot .message.user { background: #eef5ff !important; } |
|
|
.gr-chatbot .message.bot { background: #f7f8fb !important; } |
|
|
""" |
|
|
|
|
|
with gr.Blocks(title="์จํ SW๋ฏธ๋์์ฌ์ปดํจํฐํ์ โ ์์ฝยท์ถ์ฒยทQ&A (HF Router)", css=CSS, theme=gr.themes.Soft()) as demo: |
|
|
with gr.Column(): |
|
|
gr.HTML(f""" |
|
|
<div class="toss-header"> |
|
|
<div class="toss-title">์จํ SW๋ฏธ๋์์ฌ์ปดํจํฐํ์ โ ์์ฝยท์ถ์ฒยทQ&A</div> |
|
|
<div class="toss-sub"> |
|
|
์ด/์ค/๊ณ ๋ง์ถคํ AIยท์ฝ๋ฉ โ Python ยท C ยท ์๋์ด๋
ธ ยท ์น๊ฐ๋ฐ ยท ์์ํธ์ง ยท ITQ/GTQ ยท AI/๋ฐ์ดํฐ ยท ๊ฒฝ์ง๋ํ |
|
|
<br>์๋ํฌ์ธํธ: <code>{API_BASE}</code> ยท ๋ชจ๋ธ: <code>{MODEL_QA}</code> |
|
|
</div> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.Tab("์์ฝ ยท ์ถ์ฒ"): |
|
|
with gr.Row(): |
|
|
|
|
|
with gr.Column(scale=5): |
|
|
with gr.Group(elem_classes=["toss-card", "toss-input"]): |
|
|
gr.Markdown("### ๋ง์ถค ์ถ์ฒ") |
|
|
grade = gr.Radio(["์ด๋ฑ", "์ค๋ฑ", "๊ณ ๋ฑ", "์ผ๋ฐ"], value="์ค๋ฑ", label="ํ๋
") |
|
|
focus = gr.CheckboxGroup( |
|
|
["ํ์ด์ฌ", "C", "์๋์ด๋
ธ", "์น๊ฐ๋ฐ", "์์ํธ์ง", |
|
|
"์ฝ๋ฉ์๊ฒฉ์ฆ(ITQ/GTQ)", "AI/๋ฐ์ดํฐ", "๊ฒฝ์ง๋ํ"], |
|
|
label="๊ด์ฌ ๋ถ์ผ", value=["ํ์ด์ฌ"] |
|
|
) |
|
|
level = gr.Radio(["์
๋ฌธ", "๊ธฐ์ด", "์ค๊ธ", "์ฌํ"], value="๊ธฐ์ด", label="์์ค") |
|
|
goals = gr.Textbox(label="๋ชฉํ(์: ๋ํ ์
์/์๊ฒฉ์ฆ/ํฌํธํด๋ฆฌ์ค/์ํํ๊ฐ ๋ฑ)", lines=2) |
|
|
with gr.Row(): |
|
|
weeks = gr.Slider(2, 16, value=8, step=1, label="๊ธฐ๊ฐ(์ฃผ)") |
|
|
hpw = gr.Slider(1, 6, value=2, step=1, label="์ฃผ๋น ์๊ฐ(์๊ฐ)") |
|
|
constraints = gr.Textbox(label="์ ์ฝ(์๊ฐ/์์ฐ/์ง๋ ๋ฑ)", lines=1, placeholder="์: ํ์ผ ์ ๋
๋ง ๊ฐ๋ฅ") |
|
|
with gr.Row(): |
|
|
r_temp = gr.Slider(0.0, 1.0, value=0.2, step=0.05, label="temperature") |
|
|
r_max = gr.Slider(64, 1024, value=320, step=32, label="max_tokens") |
|
|
btn_rec = gr.Button("ํ์ต ์ถ์ฒ ๋ฐ๊ธฐ", elem_classes=["toss-primary"]) |
|
|
with gr.Group(elem_classes=["toss-card"]): |
|
|
rec_out = gr.Textbox(label="์ถ์ฒ ๊ฒฐ๊ณผ", lines=18) |
|
|
|
|
|
|
|
|
with gr.Column(scale=4): |
|
|
with gr.Group(elem_classes=["toss-card", "toss-input"]): |
|
|
gr.Markdown("### ์์ฝ") |
|
|
name = gr.Textbox(label="ํ์ ์ด๋ฆ", placeholder="์: ํ๊ธธ๋") |
|
|
contact = gr.Textbox(label="์ฐ๋ฝ์ฒ", placeholder="์ ํ/์นด์นด์ค/์ด๋ฉ์ผ") |
|
|
b_grade = gr.Dropdown(["์ด๋ฑ", "์ค๋ฑ", "๊ณ ๋ฑ", "์ผ๋ฐ"], value="์ค๋ฑ", label="ํ๋
") |
|
|
b_focus = gr.CheckboxGroup( |
|
|
["ํ์ด์ฌ", "C", "์๋์ด๋
ธ", "์น๊ฐ๋ฐ", "์์ํธ์ง", |
|
|
"์ฝ๋ฉ์๊ฒฉ์ฆ(ITQ/GTQ)", "AI/๋ฐ์ดํฐ", "๊ฒฝ์ง๋ํ"], |
|
|
label="๊ด์ฌ ๋ถ์ผ", value=["ํ์ด์ฌ"] |
|
|
) |
|
|
b_level = gr.Dropdown(["์
๋ฌธ", "๊ธฐ์ด", "์ค๊ธ", "์ฌํ"], value="๊ธฐ์ด", label="๋ ๋ฒจ") |
|
|
slot = gr.Dropdown(choices=available_slots(), label="์์ฝ ์ฌ๋กฏ(์~ํ , 15/17/19์)") |
|
|
notes = gr.Textbox(label="๋น๊ณ (์ ํธ ์ฃผ์ /ํน์ด์ฌํญ)", lines=2) |
|
|
with gr.Row(): |
|
|
btn_refresh = gr.Button("์ฌ๋กฏ ์๋ก๊ณ ์นจ") |
|
|
btn_book = gr.Button("์์ฝ ํ์ ", elem_classes=["toss-primary"]) |
|
|
with gr.Group(elem_classes=["toss-card"]): |
|
|
gr.Markdown("### ํ์ฌ ์์ฝ ํํฉ") |
|
|
table = gr.Dataframe( |
|
|
headers=["์ฌ๋กฏ", "์ด๋ฆ", "ํ๋
", "๊ด์ฌ ๋ถ์ผ", "๋ ๋ฒจ", "์ฐ๋ฝ์ฒ", "๋น๊ณ ", "์์ฝ์๊ฐ"], |
|
|
value=reservation_rows(load_reservations()), |
|
|
interactive=False, |
|
|
) |
|
|
res_msg = gr.Markdown("", elem_classes=["toss-note"]) |
|
|
|
|
|
|
|
|
btn_rec.click( |
|
|
fn=recommend_plan, |
|
|
inputs=[grade, focus, level, goals, weeks, hpw, constraints, r_temp, r_max], |
|
|
outputs=[rec_out], |
|
|
) |
|
|
|
|
|
btn_refresh.click(fn=refresh_slots_update, outputs=[slot]) |
|
|
|
|
|
btn_book.click( |
|
|
fn=submit_booking, |
|
|
inputs=[name, b_grade, b_focus, b_level, contact, slot, notes], |
|
|
outputs=[res_msg, table, slot], |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("์ง๋ฌธํ๊ธฐ (์ฑํ
)"): |
|
|
chat_history = gr.State([]) |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=5): |
|
|
chat = gr.Chatbot(label="๋ํ", type="tuples", height=540, show_label=True) |
|
|
with gr.Column(scale=4): |
|
|
with gr.Group(elem_classes=["toss-card", "toss-input"]): |
|
|
q = gr.Textbox( |
|
|
label="์ง๋ฌธ ์
๋ ฅ", |
|
|
placeholder="์) ์ด๋ฑ ํ์ด์ฌ ์ฒซ ์์
์ปค๋ฆฌํ๋ผ์ 4์ฃผ๋ก ์ง์ค", |
|
|
lines=6, |
|
|
) |
|
|
with gr.Row(): |
|
|
temp = gr.Slider(0.0, 1.0, value=0.2, step=0.05, label="temperature") |
|
|
max_tok = gr.Slider(64, 1024, value=256, step=32, label="max_tokens") |
|
|
with gr.Row(): |
|
|
btn_send = gr.Button("๋ต๋ณ ์์ฑ", size="lg", elem_classes=["toss-primary"]) |
|
|
btn_clear = gr.Button("์ ๋ํ", size="lg") |
|
|
with gr.Group(elem_classes=["toss-card"]): |
|
|
gr.Markdown("**๋น ๋ฅธ ์
๋ ฅ(์ํ ์ง๋ฌธ)**") |
|
|
with gr.Row(): |
|
|
b1 = gr.Button("์ด๋ฑ ํ์ด์ฌ 4์ฃผ ์ปค๋ฆฌํ๋ผ") |
|
|
b2 = gr.Button("์ค๋ฑ ์๋์ด๋
ธ ํ๋ก์ ํธ ์์ด๋์ด 5๊ฐ") |
|
|
with gr.Row(): |
|
|
b3 = gr.Button("๊ณ ๋ฑ AI ๊ฒฝ์ง๋ํ ๋๋น ๋ก๋๋งต") |
|
|
b4 = gr.Button("์ฝ๋ฉ์๊ฒฉ์ฆ(ITQ/GTQ) ๋จ๊ธฐ ํฉ๊ฒฉ ์ ๋ต") |
|
|
b1.click(lambda: "์ด๋ฑ ํ์ด์ฌ ์ฒซ ์์
๋ถํฐ 4์ฃผ ์ปค๋ฆฌํ๋ผ์ ์ฃผ์ฐจ๋ณ ๋ชฉํ/๊ต์ฌ/๊ณผ์ ๋ก ์ ๋ฆฌํด์ค.", outputs=q) |
|
|
b2.click(lambda: "์คํ์ ์์ค์์ ๊ฐ๋ฅํ ์๋์ด๋
ธ ํ๋ก์ ํธ ์์ด๋์ด 5๊ฐ๋ฅผ ๋์ด๋/๋ถํ/ํ์ต๋ชฉํ์ ํจ๊ป ํ๋ก ์ ๋ฆฌํด์ค.", outputs=q) |
|
|
b3.click(lambda: "๊ณ ๋ฑํ์ ๊ธฐ์ค AI/๋ฐ์ดํฐ ๊ฒฝ์ง๋ํ ๋๋น ๋ก๋๋งต์ 8์ฃผ ํ๋์ผ๋ก ์์ธํ ๋ง๋ค์ด์ค.", outputs=q) |
|
|
b4.click(lambda: "ITQ(ํ๊ธ/์์
)๊ณผ GTQ ํฌํ ์ต ๋จ๊ธฐ ํฉ๊ฒฉ ์ ๋ต์ ์ฃผ์ฐจ๋ณ ํ์ต๊ณํ๊ณผ ๊ธฐ์ถ ํฌ์ธํธ๋ก ์์ฝํด์ค.", outputs=q) |
|
|
|
|
|
btn_send.click(fn=chat_qa, inputs=[q, chat_history, temp, max_tok], outputs=[chat, q, chat_history]) |
|
|
q.submit(fn=chat_qa, inputs=[q, chat_history, temp, max_tok], outputs=[chat, q, chat_history]) |
|
|
btn_clear.click(fn=reset_chat, outputs=[chat, chat_history]) |
|
|
|
|
|
gr.Markdown('<div class="toss-note">โข ๋ํ ๊ธฐ๋ก์ ์ ์ฑํ
์์ญ์ ์์๋๋ก ๋์ ๋ฉ๋๋ค.' |
|
|
'<br>โข ๊ณผ๊ธ ์ฃผ์: Router ์ฌ์ฉ๋/ํ ํฐ์ ๋ฐ๋ผ ๋น์ฉ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.</div>') |
|
|
|
|
|
|
|
|
with gr.Tab("ํ์ ์๊ฐ"): |
|
|
with gr.Group(elem_classes=["toss-card"]): |
|
|
gr.Markdown(""" |
|
|
### ์ ์จํ SW๋ฏธ๋์์ฌ์ปดํจํฐํ์์ธ๊ฐ? |
|
|
- **์ค์ ์ค์ฌ ์ปค๋ฆฌํ๋ผ**: ํ์ด์ฌยทCยท์๋์ด๋
ธยท์นยท์์ํธ์ง๊น์ง ํ๋ก์ ํธ๋ก ๋ฐฐ์ฐ๋ ๊ณผ์ |
|
|
- **๋ง์ถคํ ์ง๋**: ์ด/์ค/๊ณ ํ๋
ยท์์คยท์ง๋ก์ ๋ง์ถ ๊ฐ๋ณ ํ๋ |
|
|
- **์ค์ ์งํฅ**: ์ฝ๋ฉ ์๊ฒฉ์ฆ(ITQ/GTQ ๋ฑ)ยท๊ต๋ด์ธ ๋ํยทํฌํธํด๋ฆฌ์ค ์ค๋น |
|
|
|
|
|
### ์ถ์ฒ ํธ๋ |
|
|
1) **๊ธฐ์ด ๋ค์ง๊ธฐ**: ํ์ด์ฌ ๊ธฐ์ดยท๋ฌธ์ ํด๊ฒฐยท์๊ณ ๋ฆฌ์ฆ ์ฒดํ |
|
|
2) **๋ฉ์ดํน/IoT**: ์๋์ด๋
ธยท์ผ์ยท๊ฐ๋จํ ์๋ํ ํ๋ก์ ํธ |
|
|
3) **๋ฏธ๋์ด/๋์์ธ**: ํ๋ฆฌ๋ฏธ์ดยทํฌํ ์ตยท์ฝํ
์ธ ์ ์ |
|
|
4) **์ฌํ/๊ฒฝ์ง๋ํ**: ๋ฐ์ดํฐยทAI ๊ธฐ์ด, ๋ํ ์ค๋น ๋ฐ ์ํ ์์ฑ |
|
|
|
|
|
> ์๋ด/์ฒดํ ์์
๋ฌธ์๋ '์์ฝ ยท ์ถ์ฒ' ํญ์์ ์งํํ์ธ์. |
|
|
""") |
|
|
|
|
|
|
|
|
with gr.Tab("์ํ ์ ๊ฒ"): |
|
|
with gr.Group(elem_classes=["toss-card"]): |
|
|
diag_btn = gr.Button("์๋ํฌ์ธํธ ์ ๊ฒ", elem_classes=["toss-primary"]) |
|
|
diag_out = gr.Textbox(label="๊ฒฐ๊ณผ", lines=16) |
|
|
diag_btn.click(fn=diagnose, outputs=diag_out) |
|
|
|
|
|
gr.Markdown('<div class="toss-note">ํ๊ฒฝ๋ณ์: <code>HF_TOKEN</code> (ํ์), ' |
|
|
'<code>HF_ENDPOINT_URL</code> / <code>HF_MODEL_ID</code> (์ ํ)</div>') |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch(server_name="0.0.0.0", server_port=7860, share=True) |
|
|
|