Spaces:
Sleeping
Sleeping
Download app.py from dev-yuje/gardio_test: direct link, hf CLI and curl.
- Browser
- Download file 20.3 kB
-
https://huggingface.co/spaces/dev-yuje/gardio_test/resolve/main/app.py
- Command line
-
hf download hf://spaces/dev-yuje/gardio_test/app.py
-
curl -L -o app.py https://huggingface.co/spaces/dev-yuje/gardio_test/resolve/main/app.py
20.3 kB
| import sys | |
| # 1. ์ต์๋จ ๋ชฝํค ํจ์น - ๋ชจ๋ ์ํฌํธ๋ณด๋ค ์ต์ฐ์ | |
| import huggingface_hub | |
| if not hasattr(huggingface_hub, "HfFolder"): | |
| class MockHfFolder: | |
| def get_token(): return None | |
| def save_token(token): pass | |
| def delete_token(): pass | |
| huggingface_hub.HfFolder = MockHfFolder | |
| import gradio as gr | |
| import os | |
| import re | |
| import zipfile | |
| import tempfile | |
| import numpy as np | |
| import pandas as pd | |
| import tensorflow as tf | |
| import joblib | |
| import traceback | |
| from huggingface_hub import hf_hub_download | |
| # TF ์ต์ ํ ๊ฒฝ๊ณ ๋ฐฉ์ง ๋ฐ ์์ ์ฑ | |
| os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' | |
| # ๋ฌด๊ฑฐ์ด ๋ชจ๋ธ ์ง์ฐ ๋ก๋ฉ | |
| _models = {"predictor": None, "consultant": None} | |
| REPO_ID = "dev-yuje/gardio_test" | |
| def load_keras_model_compat(model_path): | |
| """quantization_config ์ญ์ง๋ ฌํ ์ค๋ฅ๋ฅผ config.json ํจ์น๋ก ์ฐํ""" | |
| with tempfile.TemporaryDirectory() as tmpdir: | |
| with zipfile.ZipFile(model_path, 'r') as z: | |
| z.extractall(tmpdir) | |
| config_path = os.path.join(tmpdir, 'config.json') | |
| with open(config_path, 'r', encoding='utf-8') as f: | |
| config_str = f.read() | |
| config_str = re.sub(r',\s*"quantization_config":\s*null', '', config_str) | |
| config_str = re.sub(r'"quantization_config":\s*null,?\s*', '', config_str) | |
| with open(config_path, 'w', encoding='utf-8') as f: | |
| f.write(config_str) | |
| fixed_path = model_path + '.tmp_fixed.keras' | |
| with zipfile.ZipFile(fixed_path, 'w', zipfile.ZIP_DEFLATED) as z: | |
| for root, dirs, files in os.walk(tmpdir): | |
| for file in files: | |
| fp = os.path.join(root, file) | |
| arcname = os.path.relpath(fp, tmpdir) | |
| z.write(fp, arcname) | |
| model = tf.keras.models.load_model(fixed_path, compile=False) | |
| os.remove(fixed_path) | |
| return model | |
| def load_all_models(): | |
| if _models["predictor"] is None: | |
| try: | |
| # ๋ด๋ถ ์์ธก ๋ก์ง (Self-contained) | |
| class RobustCreditPredictor: | |
| def __init__(self): | |
| self.preprocessor_path = "models/preprocessor.pkl" | |
| self.model_path = "models/telecom_cb_model.keras" | |
| self.preprocessor = None | |
| self.model = None | |
| self.load_error = "์ด๊ธฐํ๋จ" | |
| self.load_resources() | |
| def load_resources(self): | |
| try: | |
| # [๊ฐ์ ] ๋ก์ปฌ ํ์ผ ์ฐ์ ๋ก๋ โ ์์ผ๋ฉด HuggingFace ๋ค์ด๋ก๋ | |
| # --- ์ ์ฒ๋ฆฌ๊ธฐ ๋ก๋ --- | |
| try: | |
| if os.path.exists(self.preprocessor_path): | |
| self.preprocessor = joblib.load(self.preprocessor_path) | |
| print(f"โ ์ ์ฒ๋ฆฌ๊ธฐ ๋ก์ปฌ ๋ก๋ ์ฑ๊ณต: {self.preprocessor_path}") | |
| else: | |
| cached_preprocessor = hf_hub_download(repo_id=REPO_ID, filename=self.preprocessor_path, repo_type="space") | |
| self.preprocessor = joblib.load(cached_preprocessor) | |
| print(f"โ ์ ์ฒ๋ฆฌ๊ธฐ HuggingFace ๋ค์ด๋ก๋ ์ฑ๊ณต") | |
| except Exception as prep_e: | |
| self.load_error = f"์ ์ฒ๋ฆฌ๊ธฐ ๋ก๋ ์คํจ: {prep_e}" | |
| return | |
| # --- ๋ชจ๋ธ ๋ก๋ --- | |
| try: | |
| if os.path.exists(self.model_path): | |
| target_path = self.model_path | |
| print(f"โ ๋ชจ๋ธ ๋ก์ปฌ ๊ฒฝ๋ก ์ฌ์ฉ: {target_path}") | |
| else: | |
| target_path = hf_hub_download(repo_id=REPO_ID, filename=self.model_path, repo_type="space") | |
| print(f"โ ๋ชจ๋ธ HuggingFace ๋ค์ด๋ก๋ ์๋ฃ") | |
| fsize = os.path.getsize(target_path) | |
| if fsize < 1000: | |
| self.load_error = f"ํ์ผ์ด ๋๋ฌด ์์({fsize}B). LFS ํฌ์ธํฐ์ผ ๊ฐ๋ฅ์ฑ ์์." | |
| return | |
| self.model = load_keras_model_compat(target_path) | |
| self.load_error = "์ฑ๊ณต" | |
| print(f"โ ๋ชจ๋ธ ๋ก๋ ์ฑ๊ณต (ํ์ผ ํฌ๊ธฐ: {fsize:,}B)") | |
| except Exception as model_e: | |
| self.load_error = f"๋ชจ๋ธ ๋ก๋ ์คํจ: {str(model_e)[:300]}" | |
| except Exception as e: | |
| self.load_error = f"๋ฆฌ์์ค ๋ก๋ ํตํฉ ์๋ฌ: {e}" | |
| def predict(self, features_dict): | |
| try: | |
| if self.model is None or self.preprocessor is None: | |
| err_short = self.load_error[:500] if self.load_error else "์์ธ ๋ถ๋ช " | |
| return f"Error: ๋ก๋ ์ํ ํ์ธ ์๋ง. ์๋ณธ ์๋ฌ: {err_short}" | |
| ALL_FEATURES = [ | |
| 'C1Z001386', 'C1M210000', 'C18210000', 'C1L120001', 'C1L120004', | |
| 'L10210000', 'L90210100', 'L90210200', 'L10210B00', 'L10216000', | |
| 'L10217000', 'D10110000', 'D10133000', 'PERF1' | |
| ] | |
| input_values = [float(features_dict.get(col, 0.0)) for col in ALL_FEATURES] | |
| df = pd.DataFrame([input_values], columns=ALL_FEATURES) | |
| log_cols = ['C1Z001386', 'C1L120004', 'D10110000', 'D10133000', 'L90210200', | |
| 'L10216000', 'L10210B00', 'L10217000', 'L90210100', 'L10210000'] | |
| df[log_cols] = np.log1p(df[log_cols].astype(float).clip(lower=0)) | |
| scaled_data = self.preprocessor.transform(df) | |
| prediction = self.model.predict(scaled_data, verbose=0) | |
| return float(prediction[0][0]) | |
| except Exception as e: | |
| return f"Error: ์์ธก ์ฐ์ฐ ์๋ฌ: {str(e)}" | |
| _models["predictor"] = RobustCreditPredictor() | |
| # ์๋ด์ฌ ๋ก์ง | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| class Consultant: | |
| def __init__(self): | |
| api_key = os.getenv("GOOGLE_API_KEY", "") | |
| from config import LLM_MODEL | |
| # [์์ ] ๋ชจ๋ธ ๋ช ์นญ ๋ฐ API ๋ฒ์ ํธํ์ฑ ๊ณ ๋ ค (config ์ฐ๋) | |
| self.llm = ChatGoogleGenerativeAI( | |
| model=LLM_MODEL, | |
| google_api_key=api_key, | |
| temperature=0.7, | |
| convert_system_message_to_human=True | |
| ) | |
| self.embedding_model = None | |
| self.retriever = None | |
| def lazy_load_search(self): | |
| if self.embedding_model is None: | |
| try: | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| from langchain_community.vectorstores import FAISS | |
| from config import EMBEDDING_MODEL, FAISS_PATH, RETRIEVER_K | |
| self.embedding_model = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL) | |
| if os.path.exists(FAISS_PATH): | |
| self.vectorstore = FAISS.load_local(FAISS_PATH, self.embedding_model, allow_dangerous_deserialization=True) | |
| self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": RETRIEVER_K}) | |
| except: pass | |
| _models["consultant"] = Consultant() | |
| except Exception as e: | |
| print(f"Grand Load Error: {e}") | |
| FEATURES_DETAIL = { | |
| 'C1Z001386': ('1๋ ๋ด ์นด๋ ์ด ์ด์ฉ๊ธ์ก', '๋ง์ ๋จ์', '0'), | |
| 'C1M210000': ('๋ณด์ ์ ์ฉ์นด๋ ์', '๊ฐ์', '0'), | |
| 'C18210000': ('๋ณด์ ์ฒดํฌ์นด๋ ์', '๊ฐ์', '0'), | |
| 'C1L120001': ('์นด๋ ์ด ํ๋๊ธ์ก', '๋ง์ ๋จ์', '0'), | |
| 'C1L120004': ('์ ์ฉ์นด๋ ๊ฐ์ค ํ ๊ฒฝ๊ณผ์ผ์', '์ผ ๋จ์', '0'), | |
| 'L90210100': ('์ํ์ ์ข ๋์ถ ๊ฑด์', '๊ฐ์', '0'), | |
| 'L90210200': ('์นด๋์ ์ข ๋์ถ ๊ฑด์', '๊ฐ์', '0'), | |
| 'L10210B00': ('๋ณดํ์ ์ข ๋์ถ ๊ฑด์', '๊ฐ์', '0'), | |
| 'L10216000': ('์ ์ฉ๋์ถ ๊ฑด์', '๊ฐ์', '0'), | |
| 'L10217000': ('๋ด๋ณด๋์ถ ๊ฑด์', '๊ฐ์', '0'), | |
| 'D10110000': ('๊ณผ๊ฑฐ ์ฐ์ฒด ๊ฑด์', '๊ฐ์', '0'), | |
| 'D10133000': ('์ด ์ฐ์ฒด ์ํ ๊ธ์ก', '๋ง์ ๋จ์', '0'), | |
| 'PERF1': ('1๋ ๋ด 90์ผ ์ด์ ์ฐ์ฒด ๊ฒฝํ', '์ฒดํฌ ์ ์ฐ์ฒด ๊ฒฝํ ์์', None), | |
| } | |
| ALL_KEYS = list(FEATURES_DETAIL.keys()) | |
| def get_grade_info(score_str): | |
| """์ ์ โ (๋ฑ๊ธ, ๋ฑ๊ธ ๊ตฌ๋ถ, ์๋ฏธ/ํน์ง, ์์) ๋ฐํ""" | |
| try: | |
| score = int(score_str) | |
| except: | |
| return None | |
| if score >= 942: grade, label, desc, color = "1๋ฑ๊ธ", "์ต์ฐ๋๋ฑ๊ธ", "์ค๋ ์ ์ฉ๊ฑฐ๋ ๊ฒฝ๋ ฅ๊ณผ ๋ค์ํ๊ณ ์ฐ๋ํ ์ ์ฉ๊ฑฐ๋ ์ค์ ์ ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋์", "#A8D8EA" | |
| elif score >= 891: grade, label, desc, color = "2๋ฑ๊ธ", "์ต์ฐ๋๋ฑ๊ธ", "์ค๋ ์ ์ฉ๊ฑฐ๋ ๊ฒฝ๋ ฅ๊ณผ ๋ค์ํ๊ณ ์ฐ๋ํ ์ ์ฉ๊ฑฐ๋ ์ค์ ์ ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋์", "#A8D8EA" | |
| elif score >= 832: grade, label, desc, color = "3๋ฑ๊ธ", "์ฐ๋๋ฑ๊ธ", "ํ๋ฐํ ์ ์ฉ๊ฑฐ๋ ์ค์ ์ ์์ผ๋, ๊ผธ์คํ ์ฐ๋ ๊ฑฐ๋๋ฅผ ์ง์ํ๋ค๋ฉด ์์๋ฑ๊ธ ์ง์ ๊ฐ๋ฅํ๋ฉฐ ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ ๋์ ์์ค์", "#B8F0C8" | |
| elif score >= 768: grade, label, desc, color = "4๋ฑ๊ธ", "์ฐ๋๋ฑ๊ธ", "ํ๋ฐํ ์ ์ฉ๊ฑฐ๋ ์ค์ ์ ์์ผ๋, ๊ผธ์คํ ์ฐ๋ ๊ฑฐ๋๋ฅผ ์ง์ํ๋ค๋ฉด ์์๋ฑ๊ธ ์ง์ ๊ฐ๋ฅํ๋ฉฐ ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ ๋์ ์์ค์", "#B8F0C8" | |
| elif score >= 698: grade, label, desc, color = "5๋ฑ๊ธ", "์ผ๋ฐ๋ฑ๊ธ", "๋น๊ต์ ๊ธ๋ฆฌ๊ฐ ๋์ ๊ธ์ต์ ๊ถ๊ณผ์ ๊ฑฐ๋๊ฐ ์๋ ๊ณ ๊ฐ์ผ๋ก, ๋จ๊ธฐ์ฐ์ฒด ๊ฒฝํ์ด ์์ผ๋ฉฐ ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ ์ผ๋ฐ์ ์ธ ์์ค์", "#FEE8A0" | |
| elif score >= 620: grade, label, desc, color = "6๋ฑ๊ธ", "์ผ๋ฐ๋ฑ๊ธ", "๋น๊ต์ ๊ธ๋ฆฌ๊ฐ ๋์ ๊ธ์ต์ ๊ถ๊ณผ์ ๊ฑฐ๋๊ฐ ์๋ ๊ณ ๊ฐ์ผ๋ก, ๋จ๊ธฐ์ฐ์ฒด ๊ฒฝํ์ด ์์ผ๋ฉฐ ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ ์ผ๋ฐ์ ์ธ ์์ค์", "#FEE8A0" | |
| elif score >= 530: grade, label, desc, color = "7๋ฑ๊ธ", "์ฃผ์๋ฑ๊ธ", "๋น๊ต์ ๊ธ๋ฆฌ๊ฐ ๋์ ๊ธ์ต์ ๊ถ๊ณผ์ ๊ฑฐ๋๊ฐ ๋ง์ ๊ณ ๊ฐ์ผ๋ก, ๋จ๊ธฐ์ฐ์ฒด ๊ฒฝํ์ ๋น๊ต์ ๋ง์ด ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋์", "#FFCB9A" | |
| elif score >= 454: grade, label, desc, color = "8๋ฑ๊ธ", "์ฃผ์๋ฑ๊ธ", "๋น๊ต์ ๊ธ๋ฆฌ๊ฐ ๋์ ๊ธ์ต์ ๊ถ๊ณผ์ ๊ฑฐ๋๊ฐ ๋ง์ ๊ณ ๊ฐ์ผ๋ก, ๋จ๊ธฐ์ฐ์ฒด ๊ฒฝํ์ ๋น๊ต์ ๋ง์ด ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋์", "#FFCB9A" | |
| elif score >= 335: grade, label, desc, color = "9๋ฑ๊ธ", "์ํ๋ฑ๊ธ", "ํ์ฌ ์ฐ์ฒด ์ค์ด๊ฑฐ๋ ๋งค์ฐ ์ฌ๊ฐํ ์ฐ์ฒด ๊ฒฝํ์ ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋์", "#FFB3B3" | |
| else: grade, label, desc, color = "10๋ฑ๊ธ", "์ํ๋ฑ๊ธ", "ํ์ฌ ์ฐ์ฒด ์ค์ด๊ฑฐ๋ ๋งค์ฐ ์ฌ๊ฐํ ์ฐ์ฒด ๊ฒฝํ์ ๋ณด์ ํ๊ณ ์์ด ๋ถ์คํ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋์", "#FFB3B3" | |
| return grade, label, desc, color | |
| SCORE_PLACEHOLDER = ''' | |
| <div style="border:2px dashed rgba(128,128,128,0.3); border-radius:16px; padding:24px; text-align:center; min-height:130px; display:flex; flex-direction:column; align-items:center; justify-content:center;"> | |
| <div style="font-size:36px; opacity:0.25;">๐</div> | |
| <div style="font-size:14px; opacity:0.45; margin-top:10px;">๐ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๊ณ ์ ์ ๋ถ์ํ๊ธฐ ๋ฒํผ์ ๋๋ฌ์ฃผ์ธ์</div> | |
| </div> | |
| ''' | |
| def make_score_html(score_str): | |
| if not score_str or score_str.startswith("โ") or score_str.startswith("โ ๏ธ"): | |
| return f''' | |
| <div style="border:2px solid #e74c3c33; border-radius:16px; padding:24px; text-align:center; min-height:120px; display:flex; align-items:center; justify-content:center;"> | |
| <div style="font-size:15px; color:#e74c3c;">{score_str}</div> | |
| </div>''' | |
| info = get_grade_info(score_str) | |
| if info is None: | |
| return f'<div style="padding:20px; text-align:center;">{score_str}</div>' | |
| grade, label, desc, color = info | |
| # ๋คํฌ๋ชจ๋ ํธํ: ๋ฐฐ๊ฒฝ ๋ฐํฌ๋ช , ํ ์คํธ๋ CSS inherit ๋์ ๋ช ์์ ๋คํฌ๋ชจ๋ ๊ณต์ ์๋ฌธ (prefers-color-scheme) | |
| return f''' | |
| <style> | |
| .score-card-{grade[0]} {{ background:{color}22; border:2px solid {color}; border-radius:16px; padding:24px; text-align:center; font-family:sans-serif; }} | |
| .score-num-{grade[0]} {{ font-size:64px; font-weight:900; color:{color}; letter-spacing:-2px; line-height:1; }} | |
| .score-grade-{grade[0]} {{ font-size:22px; font-weight:700; margin-top:8px; color:{color}; }} | |
| .score-desc-{grade[0]} {{ font-size:13px; line-height:1.6; opacity:0.75; }} | |
| @media (prefers-color-scheme: dark) {{ | |
| .score-desc-{grade[0]} {{ color: #ccc; }} | |
| }} | |
| @media (prefers-color-scheme: light) {{ | |
| .score-desc-{grade[0]} {{ color: #555; }} | |
| }} | |
| </style> | |
| <div class="score-card-{grade[0]}"> | |
| <div class="score-num-{grade[0]}">{score_str}์ </div> | |
| <div class="score-grade-{grade[0]}">{grade} | {label}</div> | |
| <hr style="border:none; border-top:1px solid {color}; margin:14px 0;"> | |
| <div class="score-desc-{grade[0]}">{desc}</div> | |
| </div> | |
| ''' | |
| def handle_predict(*args): | |
| try: | |
| load_all_models() | |
| features_dict = {} | |
| for i, key in enumerate(ALL_KEYS): | |
| val_raw = str(args[i]).strip().replace(",", "") | |
| if key == 'PERF1': | |
| features_dict[key] = 1.0 if (val_raw.lower() == 'true' or val_raw == '1' or args[i] is True) else 0.0 | |
| else: | |
| try: | |
| features_dict[key] = float(val_raw or 0) | |
| except: | |
| return f"โ ์ค๋ฅ: '{FEATURES_DETAIL[key][0]}' ์ซ์ ์๋", "โ" | |
| features_dict['L10210000'] = features_dict['L10216000'] + features_dict['L10217000'] | |
| res = _models["predictor"].predict(features_dict) | |
| if isinstance(res, str) and "Error" in res: | |
| return f"โ ๋ถ์ ์คํจ: {res}", make_score_html(f"โ ๋ก๋ ์คํจ: {res[:60]}") | |
| score_val = str(int(round(float(res)))) | |
| return {"features": features_dict, "score": score_val}, make_score_html(score_val) | |
| except Exception as e: | |
| return f"โ ์์คํ ์ค๋ฅ: {str(e)}", make_score_html(f"โ ์์คํ ์๋ฌ") | |
| def generate_response(chatbot, user_message, analysis_report): | |
| if not user_message: yield chatbot, ""; return | |
| # [์ต์ข ์์ ] Gradio 5์ 'Data incompatible' ์๋ฌ ํด๊ฒฐ์ ์ํด ๋ช ์์ ์ธ ๋์ ๋๋ฆฌ ํฌ๋งท ์ฌ์ฉ | |
| chatbot.append({"role": "user", "content": user_message}) | |
| chatbot.append({"role": "assistant", "content": "๐ [Retrieval] ๊ด๋ จ ๊ท์ ๋ฐ ์ง์นจ์ ๊ฒ์ํ๊ณ ์์ต๋๋ค..."}) | |
| yield chatbot, "" | |
| try: | |
| load_all_models() | |
| cons = _models["consultant"] | |
| cons.lazy_load_search() | |
| context = "" | |
| if cons.retriever: | |
| try: | |
| docs = cons.retriever.invoke(user_message) | |
| context = "\n\n".join([d.page_content for d in docs]) | |
| except: pass | |
| chatbot[-1] = {"role": "assistant", "content": "๐ง [Augmentation] ๊ฒ์๋ ์ง์์ ๋ฐํ์ผ๋ก ํ๋กฌํํธ๋ฅผ ๊ตฌ์ฑํ๊ณ ์์ต๋๋ค..."} | |
| yield chatbot, "" | |
| from llm.prompt import QA_PROMPT | |
| if isinstance(analysis_report, dict) and "features" in analysis_report: | |
| score_val = analysis_report.get("score", "๋ฏธ์ธก์ ") | |
| # L10210000์ ํ์ ์ปจ๋ผ์ผ๋ก FEATURES_DETAIL์ ์์ - ํค๊ฐ ์๋ ๊ฒ๋ง ํ์ | |
| features_text = "\n".join([f"- {FEATURES_DETAIL[k][0]}: {v}" for k, v in analysis_report["features"].items() if k in FEATURES_DETAIL]) | |
| query_text = f"โ ๊ณ ๊ฐ ์ ์ฉ ์ ์: {score_val}์ \nโ ๊ณ ๊ฐ์ ํ์ฌ ์ํ(์ ๋ ฅ๋ ์ ๋ณด):\n{features_text}\n\nโ ๊ณ ๊ฐ ์ง๋ฌธ: {user_message}" | |
| else: | |
| query_text = f"โ ๊ณ ๊ฐ ์ ์ฉ ์ ์: ์ ๋ณด ์์(์ผ๋ฐ ์ง๋ฌธ ์ํ)\nโ ์ํ: ๋ถ์์ ์งํํ์ง ์์\nโ ์ง๋ฌธ: {user_message}" | |
| full_prompt = QA_PROMPT.format(context=context, query=query_text) | |
| # [์ค์] ๋์ ๋๋ฆฌ ๋ฆฌ์คํธ๋ก ์ ๋ฌ (Gradio 5 UI ๋์) | |
| from langchain_core.messages import HumanMessage | |
| messages = [HumanMessage(content=full_prompt)] | |
| chatbot[-1] = {"role": "assistant", "content": "๐ก [Generation] ๋ต๋ณ์ ์์ฑ ์ค์ ๋๋ค...\n\n"} | |
| yield chatbot, "" | |
| answer_buffer = "" | |
| for chunk in cons.llm.stream(messages): | |
| answer_buffer += chunk.content | |
| # ๋ง์ง๋ง ๋ฉ์์ง์ ๋ด์ฉ์ ๋์ ๋๋ฆฌ ํฌ๋งท์ผ๋ก ์ ๋ฐ์ดํธ | |
| chatbot[-1] = {"role": "assistant", "content": answer_buffer} | |
| yield chatbot, "" | |
| except Exception as e: | |
| chatbot[-1] = {"role": "assistant", "content": f"โ ๏ธ ์๋ด ์๋ฌ: {str(e)}"} | |
| yield chatbot, "" | |
| with gr.Blocks(title="KCB AI Consultant") as demo: | |
| analysis_report = gr.State(None) | |
| gr.Markdown("# ๐ก๏ธ KCB AI ์ ์ฉ ์๋ด ์์คํ ") | |
| with gr.Row(equal_height=False): | |
| # ==== ์ผ์ชฝ: ์ ๋ ฅ ํจ๋ (2์ด ๊ทธ๋ฆฌ๋) ==== | |
| with gr.Column(scale=1, min_width=340): | |
| gr.HTML('<h2 style="margin:4px 0 12px 0; font-size:20px; font-weight:700;">📊 ์ ์ฉ ์งํ ์ ๋ ฅ</h2>') | |
| input_list = [] | |
| keys_no_perf = [k for k in ALL_KEYS if k != 'PERF1'] | |
| # 2์ด๋ก ์ ๋ ฅ ํ๋ ๋ฐฐ์น | |
| for i in range(0, len(keys_no_perf), 2): | |
| with gr.Row(): | |
| for key in keys_no_perf[i:i+2]: | |
| field_label, unit, _ = FEATURES_DETAIL[key] | |
| unit_short = unit.replace(" ๋จ์", "").replace("๊ฐ์", "๊ฐ") | |
| # label์ ๋จ์๋ฅผ ๊ดดํธ๋ก ๋ถ์ด๋, info ์์ ๋ ์ด์์ ์ ๋ฆฌ | |
| tb = gr.Textbox( | |
| label=f"{field_label} ({unit_short})", | |
| placeholder="0", | |
| min_width=120 | |
| ) | |
| input_list.append(tb) | |
| # PERF1 ์ผฌ ์ฝ๋๋ก ๋ฐฐ์น | |
| with gr.Row(): | |
| perf_cb = gr.Checkbox(label=FEATURES_DETAIL['PERF1'][0], info=FEATURES_DETAIL['PERF1'][1], value=False) | |
| input_list.append(perf_cb) | |
| predict_btn = gr.Button("๐ ์ ์ ๋ถ์ํ๊ธฐ", variant="primary", size="lg") | |
| # ==== ์ค๋ฅธ์ชฝ: ๊ฒฐ๊ณผ ํจ๋ ==== | |
| with gr.Column(scale=2): | |
| gr.HTML('<h2 style="margin:0 0 8px 0; font-size:22px; font-weight:700;">๐ฏ AI ์์ธก ์ ์ฉ ์ ์</h2>') | |
| result_display = gr.HTML(value=SCORE_PLACEHOLDER) | |
| chatbot = gr.Chatbot(label="AI ์๋ด์ฌ", height=430) | |
| with gr.Row(): | |
| msg = gr.Textbox(placeholder="์ง๋ฌธ์ ์ ๋ ฅํ์ธ์ (์ ์ฉ์ ์ ๋ถ์ ํ์๋ ๊ฐ์ธํ ์๋ด์ด ๊ฐ๋ฅํฉ๋๋ค)", show_label=False, scale=8) | |
| submit_btn = gr.Button("์ ์ก", variant="primary", scale=1) | |
| predict_btn.click(handle_predict, inputs=input_list, outputs=[analysis_report, result_display]) | |
| msg.submit(generate_response, inputs=[chatbot, msg, analysis_report], outputs=[chatbot, msg]) | |
| submit_btn.click(generate_response, inputs=[chatbot, msg, analysis_report], outputs=[chatbot, msg]) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |