Spaces:
Running
Running
| import json | |
| import os | |
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| # ---- Load config từ model.json ---- | |
| with open(os.path.join(BASE_DIR, "model.json"), "r", encoding="utf-8") as f: | |
| MODEL_CFG = json.load(f) | |
| MODEL_ID = MODEL_CFG["model_id"] | |
| # ---- Load system prompt mặc định từ systemprompt.md ---- | |
| with open(os.path.join(BASE_DIR, "systemprompt.md"), "r", encoding="utf-8") as f: | |
| DEFAULT_SYSTEM_PROMPT = f.read().strip() | |
| # ---- Load banner HTML ---- | |
| with open(os.path.join(BASE_DIR, "public", "index.html"), "r", encoding="utf-8") as f: | |
| BANNER_HTML = f.read() | |
| def respond( | |
| message, | |
| history: list[dict[str, str]], | |
| system_message, | |
| max_tokens, | |
| temperature, | |
| top_p, | |
| hf_token: gr.OAuthToken, | |
| ): | |
| client = InferenceClient(token=hf_token.token, model=MODEL_ID) | |
| messages = [{"role": "system", "content": system_message}] | |
| messages.extend(history) | |
| messages.append({"role": "user", "content": message}) | |
| response = "" | |
| for chunk in client.chat_completion( | |
| messages, | |
| max_tokens=max_tokens, | |
| stream=True, | |
| temperature=temperature, | |
| top_p=top_p, | |
| ): | |
| choices = chunk.choices | |
| token = "" | |
| if len(choices) and choices[0].delta.content: | |
| token = choices[0].delta.content | |
| response += token | |
| yield response | |
| # ---- CSS tùy chỉnh ---- | |
| custom_css = """ | |
| #chatbot-container { max-width: 900px; margin: 0 auto; } | |
| .gradio-container { font-family: 'Inter', 'Segoe UI', sans-serif !important; } | |
| .message-wrap { border-radius: 14px !important; } | |
| footer { visibility: hidden } | |
| """ | |
| # ---- JS: đọc localStorage khi load trang ---- | |
| LOAD_HISTORY_JS = """ | |
| () => { | |
| try { | |
| const raw = window.localStorage.getItem("luau_chat_history"); | |
| return raw ? raw : "[]"; | |
| } catch (e) { | |
| return "[]"; | |
| } | |
| } | |
| """ | |
| # ---- JS: lưu lịch sử vào localStorage sau mỗi lượt chat ---- | |
| SAVE_HISTORY_JS = """ | |
| (history) => { | |
| try { | |
| window.localStorage.setItem("luau_chat_history", JSON.stringify(history)); | |
| } catch (e) { | |
| console.error("Không thể lưu lịch sử chat:", e); | |
| } | |
| return history; | |
| } | |
| """ | |
| # ---- JS: xoá lịch sử ---- | |
| CLEAR_HISTORY_JS = """ | |
| () => { | |
| try { | |
| window.localStorage.removeItem("luau_chat_history"); | |
| } catch (e) {} | |
| return []; | |
| } | |
| """ | |
| def parse_stored_history(raw_json): | |
| try: | |
| data = json.loads(raw_json) | |
| if isinstance(data, list): | |
| return data | |
| except Exception: | |
| pass | |
| return [] | |
| chatbot_ui = gr.Chatbot( | |
| label=MODEL_CFG.get("display_name", "Roblox Luau Assistant"), | |
| height=550, | |
| show_copy_button=True, | |
| avatar_images=(None, MODEL_CFG.get("avatar_url")), | |
| bubble_full_width=False, | |
| render_markdown=True, | |
| type="messages", | |
| ) | |
| with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="violet")) as demo: | |
| gr.HTML(BANNER_HTML) | |
| with gr.Sidebar(): | |
| gr.Markdown("### 🔑 Tài khoản") | |
| gr.LoginButton() | |
| # State ẩn để chứa dữ liệu lịch sử đọc từ localStorage lúc khởi động | |
| stored_history_json = gr.Textbox(visible=False) | |
| with gr.Column(elem_id="chatbot-container"): | |
| chat = gr.ChatInterface( | |
| respond, | |
| chatbot=chatbot_ui, | |
| type="messages", | |
| additional_inputs=[ | |
| gr.Textbox( | |
| value=DEFAULT_SYSTEM_PROMPT, | |
| label="System message", | |
| lines=4, | |
| ), | |
| gr.Slider( | |
| minimum=1, | |
| maximum=MODEL_CFG.get("max_tokens_limit", 4096), | |
| value=MODEL_CFG.get("default_max_tokens", 1024), | |
| step=1, | |
| label="Max new tokens", | |
| ), | |
| gr.Slider( | |
| minimum=0.1, | |
| maximum=2.0, | |
| value=MODEL_CFG.get("default_temperature", 0.7), | |
| step=0.1, | |
| label="Temperature", | |
| ), | |
| gr.Slider( | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=MODEL_CFG.get("default_top_p", 0.95), | |
| step=0.05, | |
| label="Top-p (nucleus sampling)", | |
| ), | |
| ], | |
| additional_inputs_accordion=gr.Accordion(label="⚙️ Cài đặt nâng cao", open=False), | |
| examples=[ | |
| ["Viết một script Luau spawn item ngẫu nhiên mỗi 10 giây"], | |
| ["Giải thích RemoteEvent và cách dùng an toàn trong Roblox"], | |
| ["Sửa lỗi trong đoạn code Luau sau: ..."], | |
| ], | |
| cache_examples=False, | |
| ) | |
| clear_btn = gr.Button("🗑️ Xoá lịch sử chat (localStorage)") | |
| # Khi trang load: đọc localStorage -> đổ vào chatbot | |
| def _load_into_chatbot(raw_json): | |
| return parse_stored_history(raw_json) | |
| demo.load( | |
| fn=None, | |
| inputs=None, | |
| outputs=stored_history_json, | |
| js=LOAD_HISTORY_JS, | |
| ).then( | |
| fn=_load_into_chatbot, | |
| inputs=stored_history_json, | |
| outputs=chatbot_ui, | |
| ) | |
| # Sau mỗi lần chatbot_ui thay đổi (có phản hồi mới) -> lưu xuống localStorage | |
| chatbot_ui.change( | |
| fn=None, | |
| inputs=chatbot_ui, | |
| outputs=None, | |
| js=SAVE_HISTORY_JS, | |
| ) | |
| # Nút xoá lịch sử: xoá localStorage + xoá chatbot trên UI | |
| clear_btn.click( | |
| fn=None, | |
| inputs=None, | |
| outputs=chatbot_ui, | |
| js=CLEAR_HISTORY_JS, | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |