Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| EVA Mirror Space v5 - QWEN UPDATE + WHISPER FIX + QWEN3-ASR + GGUF | |
| - Qwen3 LLM: löscht alte fehlerhafte model_q4f16 Dateien, spiegelt ORT-GenAI Dateien | |
| - Whisper: löscht whisper-medium (EN-only) und altes whisper-tiny (2-Datei-System), | |
| spiegelt whisper-tiny + whisper-base im gemergten fp32-Zielformat | |
| - Qwen3-ASR-0.6B (ONNX): spiegelt das komplette Community-ONNX-Repo neu dazu | |
| - GGUF (neu in v5): legt llm/gguf/ an, spiegelt Q4_K_M-Quants von Qwen3.5-9B, | |
| Qwen3.5-0.8B (mit Fallback-Quellen), Qwen3-1.7B, Qwen3-0.6B und Qwen3-ASR-0.6B, | |
| plus mmproj-F16 für die multimodalen Qwen3.5-Modelle | |
| Läuft auf HF-Servern (Space). Kein lokales/mobiles Datenvolumen. | |
| """ | |
| import os | |
| import io | |
| import requests | |
| import gradio as gr | |
| from huggingface_hub import HfApi | |
| HF_TOKEN = os.environ.get("HF_TOKEN", "") | |
| ZIEL_REPO = "Lafu01/eva-assets" | |
| REPO_TYPE = "model" | |
| # ============================================================================= | |
| # TEIL 1: QWEN3 LLM (unverändert aus v3) | |
| # ============================================================================= | |
| QWEN_MODELLE = [ | |
| {"name": "qwen3-0.6b", "quelle": "onnx-community/Qwen3-0.6B-ONNX"}, | |
| {"name": "qwen3-1.7b", "quelle": "onnx-community/Qwen3-1.7B-ONNX"}, | |
| {"name": "qwen3-4b", "quelle": "onnx-community/Qwen3-4B-ONNX"}, | |
| ] | |
| QWEN_UNTERORDNER = "onnxruntime/cpu_and_mobile/cpu-int4-kld-block-128" | |
| QWEN_LOESCHEN = [ | |
| "llm/qwen3-0.6b/model_q4f16.onnx", | |
| "llm/qwen3-0.6b/model_q4f16.onnx_data", | |
| "llm/qwen3-1.7b/model_q4f16.onnx", | |
| "llm/qwen3-1.7b/model_q4f16.onnx_data", | |
| "llm/qwen3-4b/model_q4f16.onnx", | |
| "llm/qwen3-4b/model_q4f16.onnx_data", | |
| ] | |
| # ============================================================================= | |
| # TEIL 2: WHISPER (neu in v4) | |
| # ============================================================================= | |
| # Alte, falsche Whisper-Dateien werden per Prefix gefunden und gelöscht | |
| # (nicht hart codiert, da die genauen Dateinamen im Ziel-Repo variieren) | |
| WHISPER_LOESCH_PREFIXE = [ | |
| "stt/whisper-medium/", # EN-only, wird nicht mehr gebraucht | |
| "stt/whisper/Whisper_tiny(FB32)/", # altes 2-Datei-System (falsch) | |
| ] | |
| # Nur die GEMERGTE fp32-Variante (Ziel-System) - kein altes 2-Datei-System, | |
| # keine fp16/int8/q4/bnb4/uint8 Nebenvarianten | |
| WHISPER_ONNX_DATEIEN = ["encoder_model.onnx", "decoder_model_merged.onnx"] | |
| WHISPER_ROOT_DATEIEN = [ | |
| "added_tokens.json", | |
| "config.json", | |
| "generation_config.json", | |
| "merges.txt", | |
| "normalizer.json", | |
| "preprocessor_config.json", | |
| "special_tokens_map.json", | |
| "tokenizer.json", | |
| "tokenizer_config.json", | |
| "vocab.json", | |
| ] | |
| WHISPER_MODELLE = [ | |
| {"name": "whisper-tiny", "quelle": "onnx-community/whisper-tiny", "ziel_ordner": "stt/whisper-tiny"}, | |
| {"name": "whisper-base", "quelle": "onnx-community/whisper-base", "ziel_ordner": "stt/whisper-base"}, | |
| ] | |
| # ============================================================================= | |
| # TEIL 3: QWEN3-ASR (neu in v4) | |
| # ============================================================================= | |
| QWEN_ASR_QUELLE = "baicai1145/Qwen3-ASR-0.6B-ONNX" | |
| QWEN_ASR_ZIEL_ORDNER = "llm/qwen3-asr-0.6b" | |
| # ============================================================================= | |
| # TEIL 4: GGUF (neu in v5) - alle in Q4_K_M | |
| # ============================================================================= | |
| GGUF_ZIEL_PREFIX = "llm/gguf" | |
| MMPROJ_PRAEZISION = "f16" # F16 statt BF16: kein natives BF16 auf Snapdragon Gen 1 | |
| # Pro Modell: Liste von Quellen (erste, die eine passende Datei hat, gewinnt). | |
| # "mmproj" = True nur bei den multimodalen Qwen3.5-Modellen. | |
| GGUF_MODELLE = [ | |
| { | |
| "name": "qwen3.5-9b", | |
| "quellen": ["unsloth/Qwen3.5-9B-GGUF"], | |
| "mmproj": True, | |
| }, | |
| { | |
| "name": "qwen3.5-0.8b", | |
| "quellen": [ | |
| "unsloth/Qwen3.5-0.8B-GGUF", | |
| "bartowski/Qwen_Qwen3.5-0.8B-GGUF", | |
| "mradermacher/Qwen3.5-0.8B-GGUF", | |
| ], | |
| "mmproj": True, | |
| }, | |
| { | |
| "name": "qwen3-1.7b", | |
| "quellen": ["unsloth/Qwen3-1.7B-GGUF"], | |
| "mmproj": False, | |
| }, | |
| { | |
| "name": "qwen3-0.6b", | |
| "quellen": ["unsloth/Qwen3-0.6B-GGUF"], | |
| "mmproj": False, | |
| }, | |
| { | |
| "name": "qwen3-asr-0.6b", | |
| # bereits als Q4_K_M vor-quantisiert von OpenVoiceOS, fuer Edge/CPU gebaut | |
| "quellen": ["OpenVoiceOS/qwen3-asr-0.6b-q4-k-m"], | |
| "mmproj": False, | |
| }, | |
| ] | |
| def datei_nach_muster_finden(dateien, enthaelt, ausschliessen=None): | |
| """Sucht in einer Dateiliste (case-insensitive) nach einem Treffer. | |
| Nutzt Wortgrenzen fuer die Praezisions-Tokens (f16/bf16/f32 etc.), damit | |
| 'f16' NICHT versehentlich in 'bf16' matcht (das war ein Bug bis v5).""" | |
| import re | |
| ausschliessen = ausschliessen or [] | |
| def token_passt(token, dateiname_klein): | |
| # Grenzen: kein Buchstabe/Ziffer direkt davor oder danach | |
| pattern = r'(?<![a-z0-9])' + re.escape(token.lower()) + r'(?![a-z0-9])' | |
| return re.search(pattern, dateiname_klein) is not None | |
| for f in dateien: | |
| fl = f.lower() | |
| if all(token_passt(s, fl) for s in enthaelt) and not any(token_passt(s, fl) for s in ausschliessen): | |
| return f | |
| return None | |
| # ============================================================================= | |
| # Hilfsfunktionen | |
| # ============================================================================= | |
| def stream_kopieren(api, url, ziel_pfad, token): | |
| """Streamt direkt von URL zu HF-Repo — kein lokaler Speicher.""" | |
| try: | |
| r = requests.get(url, stream=True, timeout=300, | |
| headers={"Authorization": f"Bearer {token}"}) | |
| if r.status_code == 404: | |
| return f"FEHLER 404: {url}" | |
| r.raise_for_status() | |
| groesse_mb = int(r.headers.get("content-length", 0)) / 1024 / 1024 | |
| puffer = io.BytesIO() | |
| for chunk in r.iter_content(chunk_size=1024 * 1024): | |
| puffer.write(chunk) | |
| puffer.seek(0) | |
| api.upload_file( | |
| path_or_fileobj=puffer, | |
| path_in_repo=ziel_pfad, | |
| repo_id=ZIEL_REPO, | |
| repo_type=REPO_TYPE, | |
| token=token, | |
| ) | |
| return f"OK ({groesse_mb:.1f} MB): {ziel_pfad}" | |
| except Exception as e: | |
| return f"FEHLER: {ziel_pfad} — {e}" | |
| def hf_url(repo, pfad): | |
| return f"https://huggingface.co/{repo}/resolve/main/{pfad}" | |
| def dateien_loeschen_per_prefix(api, token, vorhanden, prefixe, log): | |
| for prefix in prefixe: | |
| treffer = [f for f in vorhanden if f.startswith(prefix)] | |
| if not treffer: | |
| log.append(f" SKIP (nichts gefunden unter): {prefix}") | |
| continue | |
| for pfad in treffer: | |
| try: | |
| api.delete_file(path_in_repo=pfad, repo_id=ZIEL_REPO, repo_type=REPO_TYPE, token=token) | |
| log.append(f" GELÖSCHT: {pfad}") | |
| vorhanden.discard(pfad) | |
| except Exception as e: | |
| log.append(f" Löschen fehlgeschlagen: {pfad} — {e}") | |
| # ============================================================================= | |
| # Ablauf: Qwen3 LLM | |
| # ============================================================================= | |
| def qwen_llm_starten(token_eingabe): | |
| token = HF_TOKEN or token_eingabe.strip() | |
| if not token: | |
| return "FEHLER: Kein Token vorhanden." | |
| api = HfApi(token=token) | |
| log = [] | |
| try: | |
| vorhanden = set(api.list_repo_files(ZIEL_REPO, repo_type=REPO_TYPE, token=token)) | |
| except Exception as e: | |
| return f"FEHLER: Repo nicht erreichbar — {e}" | |
| log.append(f"Repo: {ZIEL_REPO} | {len(vorhanden)} Dateien vorhanden") | |
| log.append("=" * 60) | |
| log.append("\n🗑️ SCHRITT 1: Lösche alte model_q4f16 Dateien...") | |
| for pfad in QWEN_LOESCHEN: | |
| if pfad in vorhanden: | |
| try: | |
| api.delete_file(path_in_repo=pfad, repo_id=ZIEL_REPO, repo_type=REPO_TYPE, token=token) | |
| log.append(f" GELÖSCHT: {pfad}") | |
| except Exception as e: | |
| log.append(f" Löschen fehlgeschlagen: {pfad} — {e}") | |
| else: | |
| log.append(f" SKIP (bereits gelöscht): {pfad}") | |
| log.append("\n🤖 SCHRITT 2: Kopiere neue ORT-GenAI Qwen3-Dateien...") | |
| for modell in QWEN_MODELLE: | |
| name = modell["name"] | |
| quelle = modell["quelle"] | |
| log.append(f"\n [{name.upper()}]") | |
| try: | |
| quell_dateien = api.list_repo_files(quelle, repo_type="model") | |
| relevante_dateien = [f for f in quell_dateien if f.startswith(QWEN_UNTERORDNER + "/")] | |
| if not relevante_dateien: | |
| log.append(f" ⚠️ HINWEIS: Keine Dateien im Ordner '{QWEN_UNTERORDNER}' auf {quelle} gefunden.") | |
| continue | |
| for voller_quell_pfad in relevante_dateien: | |
| datei_name = voller_quell_pfad.replace(QWEN_UNTERORDNER + "/", "") | |
| ziel_pfad = f"llm/{name}/{datei_name}" | |
| if ziel_pfad in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {datei_name}") | |
| continue | |
| url = hf_url(quelle, voller_quell_pfad) | |
| log.append(f" Kopiere: {datei_name} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad, token) | |
| log.append(f" {ergebnis}") | |
| except Exception as e: | |
| log.append(f" 💥 Fehler beim Auslesen der Quelle {quelle}: {e}") | |
| log.append("\n" + "=" * 60) | |
| log.append("✅ Qwen3-LLM-Update abgeschlossen!") | |
| return "\n".join(log) | |
| # ============================================================================= | |
| # Ablauf: Whisper | |
| # ============================================================================= | |
| def whisper_starten(token_eingabe): | |
| token = HF_TOKEN or token_eingabe.strip() | |
| if not token: | |
| return "FEHLER: Kein Token vorhanden." | |
| api = HfApi(token=token) | |
| log = [] | |
| try: | |
| vorhanden = set(api.list_repo_files(ZIEL_REPO, repo_type=REPO_TYPE, token=token)) | |
| except Exception as e: | |
| return f"FEHLER: Repo nicht erreichbar — {e}" | |
| log.append(f"Repo: {ZIEL_REPO} | {len(vorhanden)} Dateien vorhanden") | |
| log.append("=" * 60) | |
| log.append("\n🗑️ SCHRITT 1: Lösche whisper-medium + altes whisper-tiny...") | |
| dateien_loeschen_per_prefix(api, token, vorhanden, WHISPER_LOESCH_PREFIXE, log) | |
| log.append("\n🎙️ SCHRITT 2: Kopiere whisper-tiny + whisper-base (gemergt, fp32)...") | |
| for modell in WHISPER_MODELLE: | |
| name = modell["name"] | |
| quelle = modell["quelle"] | |
| ziel_ordner = modell["ziel_ordner"] | |
| log.append(f"\n [{name.upper()}] Quelle: {quelle}") | |
| try: | |
| quell_dateien = set(api.list_repo_files(quelle, repo_type="model")) | |
| except Exception as e: | |
| log.append(f" 💥 Fehler beim Auslesen der Quelle {quelle}: {e}") | |
| continue | |
| # root-level Dateien (Tokenizer, Config etc.) | |
| aufgaben = [(f, f) for f in WHISPER_ROOT_DATEIEN] | |
| # onnx/ Dateien - nur die exakt gemergte fp32-Variante, keine Suffixe | |
| aufgaben += [(f"onnx/{f}", f"onnx/{f}") for f in WHISPER_ONNX_DATEIEN] | |
| for quell_rel, ziel_rel in aufgaben: | |
| if quell_rel not in quell_dateien: | |
| log.append(f" ⚠️ FEHLT in Quelle: {quell_rel} — übersprungen") | |
| continue | |
| ziel_pfad = f"{ziel_ordner}/{ziel_rel}" | |
| if ziel_pfad in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {ziel_rel}") | |
| continue | |
| url = hf_url(quelle, quell_rel) | |
| log.append(f" Kopiere: {ziel_rel} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad, token) | |
| log.append(f" {ergebnis}") | |
| log.append("\n" + "=" * 60) | |
| log.append("✅ Whisper-Update abgeschlossen!") | |
| return "\n".join(log) | |
| # ============================================================================= | |
| # Ablauf: Qwen3-ASR (komplettes Repo spiegeln) | |
| # ============================================================================= | |
| def qwen_asr_starten(token_eingabe): | |
| token = HF_TOKEN or token_eingabe.strip() | |
| if not token: | |
| return "FEHLER: Kein Token vorhanden." | |
| api = HfApi(token=token) | |
| log = [] | |
| try: | |
| vorhanden = set(api.list_repo_files(ZIEL_REPO, repo_type=REPO_TYPE, token=token)) | |
| except Exception as e: | |
| return f"FEHLER: Repo nicht erreichbar — {e}" | |
| log.append(f"Repo: {ZIEL_REPO} | {len(vorhanden)} Dateien vorhanden") | |
| log.append("=" * 60) | |
| log.append(f"\n📥 Lese Dateiliste von {QWEN_ASR_QUELLE} ...") | |
| try: | |
| quell_dateien = api.list_repo_files(QWEN_ASR_QUELLE, repo_type="model") | |
| except Exception as e: | |
| return f"FEHLER: Quelle nicht erreichbar — {e}" | |
| # HF-interne Metadateien (.gitattributes etc.) auslassen | |
| quell_dateien = [f for f in quell_dateien if not os.path.basename(f).startswith(".")] | |
| log.append(f" {len(quell_dateien)} Dateien gefunden.") | |
| log.append(f"\n🎤 Kopiere Qwen3-ASR-0.6B nach {QWEN_ASR_ZIEL_ORDNER}/ ...") | |
| for quell_rel in quell_dateien: | |
| ziel_pfad = f"{QWEN_ASR_ZIEL_ORDNER}/{quell_rel}" | |
| if ziel_pfad in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {quell_rel}") | |
| continue | |
| url = hf_url(QWEN_ASR_QUELLE, quell_rel) | |
| log.append(f" Kopiere: {quell_rel} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad, token) | |
| log.append(f" {ergebnis}") | |
| log.append("\n" + "=" * 60) | |
| log.append("✅ Qwen3-ASR-Update abgeschlossen!") | |
| return "\n".join(log) | |
| # ============================================================================= | |
| # Ablauf: GGUF (neu in v5) | |
| # ============================================================================= | |
| def gguf_starten(token_eingabe): | |
| token = HF_TOKEN or token_eingabe.strip() | |
| if not token: | |
| return "FEHLER: Kein Token vorhanden." | |
| api = HfApi(token=token) | |
| log = [] | |
| try: | |
| vorhanden = set(api.list_repo_files(ZIEL_REPO, repo_type=REPO_TYPE, token=token)) | |
| except Exception as e: | |
| return f"FEHLER: Repo nicht erreichbar — {e}" | |
| log.append(f"Repo: {ZIEL_REPO} | {len(vorhanden)} Dateien vorhanden") | |
| log.append("=" * 60) | |
| log.append(f"\n📦 GGUF-Modelle (alle Q4_K_M) nach {GGUF_ZIEL_PREFIX}/ ...") | |
| for modell in GGUF_MODELLE: | |
| name = modell["name"] | |
| ziel_ordner = f"{GGUF_ZIEL_PREFIX}/{name}" | |
| log.append(f"\n [{name.upper()}]") | |
| gefunden = False | |
| for quelle in modell["quellen"]: | |
| try: | |
| quell_dateien = api.list_repo_files(quelle, repo_type="model") | |
| except Exception as e: | |
| log.append(f" ⚠️ Quelle {quelle} nicht erreichbar ({e}) - versuche naechste...") | |
| continue | |
| quant_datei = datei_nach_muster_finden(quell_dateien, ["q4_k_m"], ausschliessen=["mmproj"]) | |
| if not quant_datei: | |
| log.append(f" ⚠️ Kein Q4_K_M in {quelle} gefunden - versuche naechste Quelle...") | |
| continue | |
| log.append(f" Quelle: {quelle}") | |
| gefunden = True | |
| # Haupt-Quant-Datei | |
| ziel_pfad = f"{ziel_ordner}/{quant_datei}" | |
| if ziel_pfad in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {quant_datei}") | |
| else: | |
| url = hf_url(quelle, quant_datei) | |
| log.append(f" Kopiere: {quant_datei} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad, token) | |
| log.append(f" {ergebnis}") | |
| # mmproj-Datei (nur bei multimodalen Qwen3.5-Modellen) | |
| if modell["mmproj"]: | |
| mmproj_datei = datei_nach_muster_finden(quell_dateien, ["mmproj", MMPROJ_PRAEZISION]) | |
| if not mmproj_datei: | |
| # Fallback: irgendeine mmproj-Datei nehmen, falls exakte Praezision fehlt | |
| mmproj_datei = datei_nach_muster_finden(quell_dateien, ["mmproj"]) | |
| if mmproj_datei: | |
| ziel_pfad_mm = f"{ziel_ordner}/{mmproj_datei}" | |
| if ziel_pfad_mm in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {mmproj_datei}") | |
| else: | |
| url = hf_url(quelle, mmproj_datei) | |
| log.append(f" Kopiere: {mmproj_datei} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad_mm, token) | |
| log.append(f" {ergebnis}") | |
| else: | |
| log.append(f" ⚠️ HINWEIS: Keine mmproj-Datei in {quelle} gefunden.") | |
| break # erste funktionierende Quelle reicht | |
| if not gefunden: | |
| log.append(f" 💥 FEHLER: In keiner Quelle eine Q4_K_M-Datei gefunden fuer {name}.") | |
| log.append("\n" + "=" * 60) | |
| log.append("✅ GGUF-Update abgeschlossen!") | |
| return "\n".join(log) | |
| # ============================================================================= | |
| # Ablauf: mmproj-Korrektur (BF16 -> F16 Bugfix) | |
| # ============================================================================= | |
| def mmproj_korrigieren(token_eingabe): | |
| """Loescht faelschlich geholte mmproj-BF16.gguf Dateien (Bug: 'f16' matchte | |
| als Teilstring von 'bf16') und holt danach mit dem gefixten Musterabgleich | |
| die korrekte mmproj-F16.gguf nach.""" | |
| token = HF_TOKEN or token_eingabe.strip() | |
| if not token: | |
| return "FEHLER: Kein Token vorhanden." | |
| api = HfApi(token=token) | |
| log = [] | |
| try: | |
| vorhanden = set(api.list_repo_files(ZIEL_REPO, repo_type=REPO_TYPE, token=token)) | |
| except Exception as e: | |
| return f"FEHLER: Repo nicht erreichbar — {e}" | |
| log.append(f"Repo: {ZIEL_REPO} | {len(vorhanden)} Dateien vorhanden") | |
| log.append("=" * 60) | |
| log.append("\n🗑️ SCHRITT 1: Loesche falsche mmproj-BF16.gguf (Bug-Fund)...") | |
| betroffene_ordner = [m["name"] for m in GGUF_MODELLE if m["mmproj"]] | |
| for name in betroffene_ordner: | |
| ziel_ordner = f"{GGUF_ZIEL_PREFIX}/{name}" | |
| falsch = [f for f in vorhanden if f.startswith(ziel_ordner + "/") and "bf16" in f.lower() and "mmproj" in f.lower()] | |
| for pfad in falsch: | |
| try: | |
| api.delete_file(path_in_repo=pfad, repo_id=ZIEL_REPO, repo_type=REPO_TYPE, token=token) | |
| log.append(f" GELÖSCHT: {pfad}") | |
| vorhanden.discard(pfad) | |
| except Exception as e: | |
| log.append(f" Löschen fehlgeschlagen: {pfad} — {e}") | |
| if not falsch: | |
| log.append(f" SKIP (keine falsche BF16-Datei in {ziel_ordner}/)") | |
| log.append("\n📦 SCHRITT 2: Hole korrekte mmproj-F16.gguf nach (gefixter Musterabgleich)...") | |
| for modell in GGUF_MODELLE: | |
| if not modell["mmproj"]: | |
| continue | |
| name = modell["name"] | |
| ziel_ordner = f"{GGUF_ZIEL_PREFIX}/{name}" | |
| log.append(f"\n [{name.upper()}]") | |
| for quelle in modell["quellen"]: | |
| try: | |
| quell_dateien = api.list_repo_files(quelle, repo_type="model") | |
| except Exception as e: | |
| log.append(f" ⚠️ Quelle {quelle} nicht erreichbar ({e}) - versuche naechste...") | |
| continue | |
| mmproj_datei = datei_nach_muster_finden(quell_dateien, ["mmproj", MMPROJ_PRAEZISION]) | |
| if not mmproj_datei: | |
| log.append(f" ⚠️ Kein echtes {MMPROJ_PRAEZISION} in {quelle} gefunden - versuche naechste Quelle...") | |
| continue | |
| ziel_pfad = f"{ziel_ordner}/{mmproj_datei}" | |
| if ziel_pfad in vorhanden: | |
| log.append(f" SKIP (bereits vorhanden): {mmproj_datei}") | |
| else: | |
| url = hf_url(quelle, mmproj_datei) | |
| log.append(f" Kopiere: {mmproj_datei} ...") | |
| ergebnis = stream_kopieren(api, url, ziel_pfad, token) | |
| log.append(f" {ergebnis}") | |
| break | |
| log.append("\n" + "=" * 60) | |
| log.append("✅ mmproj-Korrektur abgeschlossen!") | |
| return "\n".join(log) | |
| # ============================================================================= | |
| # Ablauf: Alles nacheinander | |
| # ============================================================================= | |
| def alles_starten(token_eingabe): | |
| teile = [ | |
| qwen_llm_starten(token_eingabe), | |
| whisper_starten(token_eingabe), | |
| qwen_asr_starten(token_eingabe), | |
| gguf_starten(token_eingabe), | |
| ] | |
| return ("\n\n" + "#" * 60 + "\n\n").join(teile) | |
| # ============================================================================= | |
| # Gradio GUI | |
| # ============================================================================= | |
| with gr.Blocks(title="EVA Mirror Fixer v5") as demo: | |
| gr.Markdown("## 🔧 EVA Mirror Fixer v5\nQwen3-LLM-Fix, Whisper-Fix (tiny+base, gemergt), Qwen3-ASR und GGUF-Modelle (Q4_K_M).") | |
| token_feld = gr.Textbox( | |
| label="HF Token (Write)", | |
| placeholder="hf_... (nicht nötig wenn als Space-Secret hinterlegt)", | |
| type="password" | |
| ) | |
| with gr.Row(): | |
| qwen_btn = gr.Button("1️⃣ Qwen3 LLM Fix", variant="secondary") | |
| whisper_btn = gr.Button("2️⃣ Whisper Fix", variant="secondary") | |
| asr_btn = gr.Button("3️⃣ Qwen3-ASR hinzufügen", variant="secondary") | |
| gguf_btn = gr.Button("4️⃣ GGUF hinzufügen", variant="secondary") | |
| mmproj_fix_btn = gr.Button("🩹 mmproj BF16→F16 Korrektur (9B + 0.8B)", variant="stop") | |
| alles_btn = gr.Button("🚀 Alles nacheinander ausführen", variant="primary") | |
| ausgabe = gr.Textbox(label="Aktivitäts-Log", lines=30) | |
| qwen_btn.click(fn=qwen_llm_starten, inputs=token_feld, outputs=ausgabe) | |
| whisper_btn.click(fn=whisper_starten, inputs=token_feld, outputs=ausgabe) | |
| asr_btn.click(fn=qwen_asr_starten, inputs=token_feld, outputs=ausgabe) | |
| gguf_btn.click(fn=gguf_starten, inputs=token_feld, outputs=ausgabe) | |
| mmproj_fix_btn.click(fn=mmproj_korrigieren, inputs=token_feld, outputs=ausgabe) | |
| alles_btn.click(fn=alles_starten, inputs=token_feld, outputs=ausgabe) | |
| demo.launch() | |