Spaces:
Paused
Paused
| import os | |
| import random | |
| import time | |
| import shutil | |
| from datetime import datetime, timezone | |
| import pandas as pd | |
| import gradio as gr | |
| from datasets import load_dataset, Dataset | |
| from transformers import ( | |
| AutoTokenizer, | |
| GPT2LMHeadModel, | |
| GPT2Config, | |
| TrainingArguments, | |
| Trainer, | |
| DataCollatorForLanguageModeling | |
| ) | |
| from huggingface_hub import model_info | |
| # --- CSS DO UKRYCIA NIECHCIANYCH ELEMENTÓW --- | |
| custom_css = """ | |
| .header-bar, div[data-testid="header"], .app-header, .flex-row.items-center.justify-between { | |
| display: none !important; height: 0 !important; | |
| } | |
| footer, .built-with-gradio, footer.svelte-1y4jv3v, div.svelte-1y4jv3v, .settings-btn, | |
| button[aria-label="Settings"], .gradio-container > footer, footer > *, footer a, footer button { | |
| display: none !important; visibility: hidden !important; height: 0 !important; | |
| opacity: 0 !important; pointer-events: none !important; | |
| } | |
| """ | |
| # --- USTAWIENIA --- | |
| hf_token = os.environ.get("HF_TOKEN") | |
| dataset_name = "StrawberryJelly/unimind-kronika" | |
| model_repo = "StrawberryJelly/unimind-mozg" | |
| model_dir = "./model" | |
| # --- HOT RELOAD ZMIENNE --- | |
| last_model_check = time.time() | |
| last_model_version = None | |
| # --- INIT KRONIKI --- | |
| try: | |
| kronika = load_dataset(dataset_name, split="train", token=hf_token) | |
| if len(kronika) == 0: raise ValueError("Empty dataset") | |
| except Exception: | |
| now_utc = datetime.now(timezone.utc).isoformat() | |
| init_data = [{"id": 1, "ts": now_utc, "user": "StrawberryJelly", "q": "genesis", "a": "a", "flag": "ok"}] | |
| kronika = Dataset.from_list(init_data) | |
| kronika.push_to_hub(dataset_name, token=hf_token) | |
| # --- INIT MODELU --- | |
| os.makedirs(model_dir, exist_ok=True) | |
| tokenizer = AutoTokenizer.from_pretrained("flax-community/papuGaPT2") | |
| if tokenizer.pad_token is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| try: | |
| model = GPT2LMHeadModel.from_pretrained(model_repo, token=hf_token) | |
| print("✅ Załadowano istniejący model z HF Hub.") | |
| except Exception: | |
| print("️ Brak modelu na Hubie. Tworzę losowy model od zera...") | |
| config = GPT2Config(vocab_size=50257, n_positions=1024, n_embd=768, n_layer=12, n_head=12) | |
| model = GPT2LMHeadModel(config) | |
| model.save_pretrained(model_dir) | |
| tokenizer.save_pretrained(model_dir) | |
| model.push_to_hub(model_repo, token=hf_token) | |
| tokenizer.push_to_hub(model_repo, token=hf_token) | |
| # --- CZYSZCZENIE CACHE I HOT RELOAD --- | |
| def clean_model_cache(): | |
| """Czyści lokalne pliki modelu, żeby nie zapychać dysku""" | |
| try: | |
| if os.path.exists("./tmp"): | |
| shutil.rmtree("./tmp") | |
| if os.path.exists("./model"): | |
| shutil.rmtree("./model") | |
| os.makedirs("./model", exist_ok=True) | |
| print("🧹 Lokalny folder modelu wyczyszczony.") | |
| except Exception as e: | |
| print(f"⚠️ Błąd czyszczenia folderu: {e}") | |
| def check_for_new_model(): | |
| """Sprawdź co 10 minut czy jest nowy model na HF Hub""" | |
| global model, last_model_check, last_model_version | |
| current_time = time.time() | |
| if current_time - last_model_check < 600: # Sprawdź co 10 minut | |
| return | |
| last_model_check = current_time | |
| try: | |
| info = model_info(model_repo, token=hf_token) | |
| current_version = info.lastModified | |
| if last_model_version is None: | |
| last_model_version = current_version | |
| print(f"📦 Wersja modelu: {current_version}") | |
| elif current_version != last_model_version: | |
| print(f"🔄 Nowa wersja modelu wykryta! Sprzątam i przeładowuję...") | |
| clean_model_cache() | |
| model = GPT2LMHeadModel.from_pretrained(model_repo, token=hf_token) | |
| last_model_version = current_version | |
| print(f"✅ Model przeładowany!") | |
| except Exception as e: | |
| print(f"⚠️ Błąd hot-reload: {e}") | |
| # --- STATUS I MATEMATYKA --- | |
| def get_status(): | |
| global kronika | |
| try: | |
| ds = load_dataset(dataset_name, split="train", token=hf_token) | |
| except Exception: | |
| ds = kronika | |
| total = len(ds) | |
| ok_count = len(ds.filter(lambda x: x['flag'] == 'ok')) | |
| pending_count = len(ds.filter(lambda x: x['flag'] == 'pending')) | |
| return f"UNIMIND. Stan: Przetrenowane: {ok_count} | Oczekujące: {pending_count} | Łącznie: {total}" | |
| def generate_math(): | |
| a = random.randint(2, 9) | |
| b = random.randint(2, 9) | |
| c = random.randint(1, 5) | |
| if random.choice([True, False]): | |
| question = f"{a} * {b} + {c} = ?" | |
| ans = a * b + c | |
| else: | |
| question = f"{a} * {b} - {c} = ?" | |
| ans = a * b - c | |
| return question, str(ans) | |
| # --- JAVASCRIPT DO LOCALSTORAGE --- | |
| js_save_chat = """ | |
| function(chat_history) { | |
| if (!Array.isArray(chat_history)) return []; | |
| localStorage.setItem('unimind_chat_history', JSON.stringify(chat_history)); | |
| return chat_history; | |
| }""" | |
| js_load_chat = """ | |
| function() { | |
| const saved = localStorage.getItem('unimind_chat_history'); | |
| if (saved) { | |
| try { | |
| const parsed = JSON.parse(saved); | |
| return Array.isArray(parsed) ? parsed.filter(m => typeof m === 'object' && m.role && m.content) : []; | |
| } catch(e) { return []; } | |
| } | |
| return []; | |
| }""" | |
| js_clear_chat = "function() { localStorage.removeItem('unimind_chat_history'); return []; }" | |
| # --- UI --- | |
| # POPRAWKA 1: Usunięto css z Blocks() | |
| with gr.Blocks() as demo: | |
| math_q, math_a = generate_math() | |
| math_state = gr.State(math_a) | |
| math_q_display = gr.Markdown(f"### Zabezpieczenie antybot: `{math_q}`") | |
| ans_input = gr.Textbox(label="Podaj wynik") | |
| submit_btn = gr.Button("Zatwierdź") | |
| app_container = gr.Column(visible=False) | |
| def check_ans(user_ans, correct_ans): | |
| if str(user_ans).strip() == str(correct_ans).strip(): | |
| return gr.update(visible=False), gr.update(visible=True), correct_ans, "" | |
| else: | |
| q, a = generate_math() | |
| return gr.update(value=f"### Zabezpieczenie antybot: `{q}` \n\n **Błędna odpowiedź.**"), gr.update(visible=False), a, "" | |
| submit_btn.click(check_ans, [ans_input, math_state], [math_q_display, app_container, math_state, ans_input]) | |
| with app_container: | |
| status_md = gr.Markdown(get_status()) | |
| with gr.Tabs(): | |
| with gr.TabItem("Gadaj"): | |
| gr.Warning("⚠️ Trening modelu odbywa się o 3:00, 12:00 i 22:00 czasu polskiego. W tych godzinach serwer może działać wolniej.") | |
| gr.Markdown("UWAGA: Na start będzie bełkotał losowe tokeny. To normalne.") | |
| # POPRAWKA 2: Usunięto type="messages" i show_copy_button (domyślne w Gradio 6.0) | |
| chatbot = gr.Chatbot(value=[], height=400) | |
| msg = gr.Textbox(label="Twój prompt") | |
| clear_btn = gr.Button("🗑️ Wyczyść rozmowę") | |
| def respond(message, chat_history): | |
| check_for_new_model() # Hot reload check | |
| if chat_history is None: | |
| chat_history = [] | |
| if not message or not message.strip(): | |
| return chat_history | |
| context = "" | |
| if chat_history: | |
| context = "\n--- HISTORIA ---\n" | |
| for msg_pair in chat_history[-2:]: | |
| if isinstance(msg_pair, dict): | |
| role = msg_pair.get('role', '') | |
| content = msg_pair.get('content', '') | |
| if role == 'user': context += f"Pytanie: {content}\n" | |
| elif role == 'assistant': context += f"Odpowiedź: {content}\n" | |
| context += "--- KONIEC HISTORII ---\n\n" | |
| formatted = f"{context}NOWE PYTANIE: {message}\nOdpowiedź:" | |
| inputs = tokenizer(formatted, return_tensors="pt", truncation=True, max_length=512) | |
| outputs = model.generate( | |
| **inputs, max_new_tokens=50, temperature=1.0, do_sample=True, | |
| repetition_penalty=1.2, pad_token_id=tokenizer.eos_token_id | |
| ) | |
| decoded = tokenizer.decode(outputs[0], skip_special_tokens=True) | |
| if "Odpowiedź:" in decoded: | |
| parts = decoded.split("Odpowiedź:") | |
| ans = parts[-1].strip() | |
| else: | |
| ans = decoded.strip() | |
| chat_history.append({"role": "user", "content": message}) | |
| chat_history.append({"role": "assistant", "content": ans}) | |
| return chat_history | |
| msg.submit(respond, [msg, chatbot], [chatbot]).then(None, [chatbot], None, js=js_save_chat) | |
| clear_btn.click(None, None, chatbot, js=js_clear_chat) | |
| with gr.TabItem("Naucz"): | |
| gr.Markdown("### 📝 Dodaj wpis do Kroniki") | |
| gr.Markdown("Wpis trafi do bazy z flagą 'pending'. Model nauczy się go podczas najbliższego treningu (3:00, 12:00, 22:00 PL).") | |
| q_in = gr.Textbox(label="Kiedy ktoś zapyta:") | |
| a_in = gr.Textbox(label="Unimind ma odpowiedzieć:") | |
| add_btn = gr.Button("➕ DODAJ DO KRONIKI") | |
| add_status = gr.Markdown("") | |
| def add_to_kronika(q, a): | |
| global kronika | |
| try: | |
| kronika = load_dataset(dataset_name, split="train", token=hf_token) | |
| except Exception: | |
| pass | |
| new_id = len(kronika) + 1 | |
| now_utc = datetime.now(timezone.utc).isoformat() | |
| new_row = {"id": new_id, "ts": now_utc, "user": "anon", "q": q, "a": a, "flag": "pending"} | |
| df = kronika.to_pandas() | |
| df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True) | |
| kronika = Dataset.from_pandas(df) | |
| kronika.push_to_hub(dataset_name, token=hf_token) | |
| pending_count = len(kronika.filter(lambda x: x['flag'] == 'pending')) | |
| total_count = len(kronika) | |
| return f"✅ Dodano! Oczekujących wpisów: {pending_count}/{total_count}.", get_status() | |
| add_btn.click(add_to_kronika, [q_in, a_in], [add_status, status_md]) | |
| with gr.TabItem("Kronika"): | |
| gr.Markdown(f"[Otwórz Kronikę na Hugging Face](https://huggingface.co/datasets/{dataset_name})") | |
| gr.Markdown("Zmień flag na 'rejected' żeby usunąć neuron z treningu") | |
| demo.load(None, None, chatbot, js=js_load_chat) | |
| # POPRAWKA 3: Przeniesiono css do launch() | |
| demo.launch(css=custom_css) |