Spaces:
Running
Running
| """ # build: 20260603-1016 | |
| Oneiros — Day 2: mimpi → SVG dream map + Markdown panel (Space & lokal). | |
| HF Spaces: objek `demo` harus ada di level modul (bukan hanya di __main__). | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import traceback | |
| import gradio as gr | |
| from map.format_panel import format_panel_entitas | |
| from map.generator import generate_dream_map | |
| from ui.gaya_oneiros import ( | |
| CONTOH_MIMPI, | |
| CSS_ONEIROS, | |
| PESAN_STATUS_IDLE, | |
| TEKS_FOOTER, | |
| TEKS_LANGKAH, | |
| buat_teks_header, | |
| buat_teks_latar, | |
| format_status_error, | |
| format_status_hasil, | |
| tema_oneiros, | |
| ) | |
| from ui.konstan import PANJANG_MINIMUM_MIMPI | |
| DISCLAIMER = ( | |
| "This online demo reads your dream on our servers so you can try it in the browser. " | |
| "Want to keep dreams only on your own computer? See the project README." | |
| ) | |
| def _pastikan_llama_cpp() -> bool: | |
| """Install llama-cpp-python jika tidak ada di Docker image (fallback runtime).""" | |
| try: | |
| import llama_cpp # noqa: F401 | |
| return True | |
| except ImportError: | |
| pass | |
| if not os.getenv("SPACE_ID"): | |
| return False # lokal: jangan install otomatis | |
| print("[oneiros] llama_cpp tidak ada — install runtime (1-3 menit)…") | |
| import subprocess | |
| wheel = ( | |
| "llama-cpp-python @ https://github.com/abetlen/llama-cpp-python" | |
| "/releases/download/v0.3.19/llama_cpp_python-0.3.19-cp310-cp310-linux_x86_64.whl" | |
| ) | |
| result = subprocess.run( | |
| ["pip", "install", "--no-cache-dir", "--quiet", wheel], | |
| capture_output=True, | |
| text=True, | |
| timeout=300, | |
| ) | |
| if result.returncode == 0: | |
| print("[oneiros] llama_cpp berhasil diinstall.") | |
| return True | |
| print(f"[oneiros] install llama_cpp gagal: {result.stderr[:500]}") | |
| return False | |
| def warmup_model() -> None: | |
| """Muat model sekali saat startup; aman jika preload belum selesai.""" | |
| from model.loader import ( | |
| diagnosis_lingkungan_model, | |
| get_model, | |
| get_model_path, | |
| reset_model_path_cache, | |
| ) | |
| diag = diagnosis_lingkungan_model() | |
| print(f"[oneiros] diagnosis: {diag}") | |
| if os.getenv("ONEIROS_SKIP_WARMUP") == "1": | |
| print("Warm-up dilewati (ONEIROS_SKIP_WARMUP=1).") | |
| return | |
| if not _pastikan_llama_cpp(): | |
| print("Warm-up: llama_cpp tidak tersedia, skip.") | |
| return | |
| try: | |
| reset_model_path_cache() | |
| get_model() | |
| print(f"Model siap: {get_model_path()}") | |
| except FileNotFoundError as e: | |
| print(f"Warm-up: model belum tersedia — {e}") | |
| except Exception as e: | |
| print(f"Warm-up gagal (app tetap jalan): {type(e).__name__}: {e}") | |
| def proses_mimpi(teks_mimpi: str, progress=gr.Progress()): | |
| if not teks_mimpi or len(teks_mimpi.strip()) < PANJANG_MINIMUM_MIMPI: | |
| err_html = format_status_error( | |
| f"Please write a bit more—at least {PANJANG_MINIMUM_MIMPI} characters " | |
| "(a sentence or two about what happened and how it felt)." | |
| ) | |
| return "", "", err_html | |
| try: | |
| progress(0.10, desc="Checking dream reader…") | |
| _pastikan_llama_cpp() # no-op jika sudah ada; install jika belum | |
| progress(0.20, desc="Reading your dream…") | |
| from model.extractor import extract_entities_dengan_waktu | |
| from storage.trace_logger import log_trace | |
| entities, raw, parse_ok, err, detik, raw_pertama = extract_entities_dengan_waktu( | |
| teks_mimpi | |
| ) | |
| progress(0.60, desc="Drawing the map…") | |
| except ModuleNotFoundError: | |
| err_html = format_status_error( | |
| "The dream reader is still loading. Please try again in a minute." | |
| ) | |
| return "", "", err_html | |
| except FileNotFoundError: | |
| err_html = format_status_error( | |
| "We are still waking up the dream reader. Please wait a moment and try again." | |
| ) | |
| return "", "", err_html | |
| except Exception as e: | |
| traceback.print_exc() | |
| return "", "", format_status_error( | |
| "Something went wrong while reading your dream. Please try again." | |
| ) | |
| svg = generate_dream_map(entities) | |
| panel = format_panel_entitas(entities) | |
| lingkungan = "hf_space" if os.getenv("SPACE_ID") else "local" | |
| try: | |
| log_trace( | |
| dream_text=teks_mimpi, | |
| raw_model_output=raw, | |
| entities=entities, | |
| parse_ok=parse_ok, | |
| parse_error=err, | |
| elapsed_extract=detik, | |
| elapsed_render=0.0, | |
| environment=lingkungan, | |
| raw_percobaan_pertama=raw_pertama, | |
| ) | |
| except OSError as e: | |
| print(f"Trace tidak tertulis: {e}") | |
| progress(1.0, desc="Done") | |
| return svg, panel, format_status_hasil(entities, parse_ok, err, detik) | |
| def reset_form(): | |
| return "", "", "", PESAN_STATUS_IDLE | |
| demo = gr.Blocks( | |
| title="Oneiros", | |
| theme=tema_oneiros(), | |
| css=CSS_ONEIROS, | |
| ) | |
| with demo: | |
| gr.HTML(buat_teks_latar(), elem_classes=["oneiros-latar-host"]) | |
| gr.HTML(buat_teks_header()) | |
| gr.HTML(TEKS_LANGKAH) | |
| if os.getenv("SPACE_ID"): | |
| gr.Markdown(DISCLAIMER, elem_classes=["oneiros-notice"]) | |
| if not os.getenv("ONEIROS_FULL_DEPLOY"): | |
| gr.Markdown( | |
| "The page is open, but dream reading may not work until setup finishes. " | |
| "If the button does nothing useful, try again in a few minutes.", | |
| elem_classes=["oneiros-notice"], | |
| ) | |
| with gr.Row(elem_classes=["oneiros-workspace"]): | |
| with gr.Column(scale=5, elem_classes=["oneiros-panel"]): | |
| gr.Markdown("Write your dream", elem_classes=["oneiros-panel-head"]) | |
| gr.Markdown( | |
| "Tell the story in your own words—what happened, how it felt, who was there.", | |
| elem_classes=["oneiros-panel-desc"], | |
| ) | |
| input_mimpi = gr.Textbox( | |
| label="Dream narrative", | |
| placeholder=( | |
| "I stood in a house I did not recognize, yet knew was mine. " | |
| "The walls shifted between memory and somewhere I had never been. " | |
| "Through a window that appeared and vanished, I saw a garden where " | |
| "someone I loved was planting light instead of seeds…" | |
| ), | |
| lines=12, | |
| max_lines=22, | |
| elem_classes=["oneiros-dream-input"], | |
| ) | |
| gr.Markdown( | |
| f"A few sentences work best (at least {PANJANG_MINIMUM_MIMPI} characters).", | |
| elem_classes=["oneiros-hint"], | |
| ) | |
| with gr.Row(elem_classes=["oneiros-actions"]): | |
| btn_ekstrak = gr.Button( | |
| "Understand my dream", | |
| variant="primary", | |
| elem_classes=["oneiros-btn-primary"], | |
| scale=2, | |
| ) | |
| btn_reset = gr.Button( | |
| "Clear", | |
| variant="secondary", | |
| elem_classes=["oneiros-btn-secondary"], | |
| scale=1, | |
| ) | |
| with gr.Accordion("Sample dreams", open=False, elem_classes=["oneiros-examples"]): | |
| gr.Examples(examples=CONTOH_MIMPI, inputs=[input_mimpi]) | |
| with gr.Column(scale=7, elem_classes=["oneiros-panel"]): | |
| gr.Markdown("Dream map", elem_classes=["oneiros-panel-head"]) | |
| gr.Markdown( | |
| "People, places, and images from your story—connected by feeling.", | |
| elem_classes=["oneiros-panel-desc"], | |
| ) | |
| output_svg = gr.HTML( | |
| value="", | |
| elem_classes=["oneiros-map-output"], | |
| ) | |
| output_panel = gr.Markdown( | |
| value="", | |
| elem_classes=["oneiros-entity-panel"], | |
| ) | |
| output_status = gr.HTML(value=PESAN_STATUS_IDLE) | |
| gr.HTML(TEKS_FOOTER) | |
| btn_ekstrak.click( | |
| fn=proses_mimpi, | |
| inputs=[input_mimpi], | |
| outputs=[output_svg, output_panel, output_status], | |
| show_progress=True, | |
| ) | |
| btn_reset.click( | |
| fn=reset_form, | |
| outputs=[input_mimpi, output_svg, output_panel, output_status], | |
| ) | |
| def _jalankan_warmup_space() -> None: | |
| """Warm-up di background pada Space agar startup tidak hang.""" | |
| if not os.getenv("SPACE_ID"): | |
| return | |
| if os.getenv("ONEIROS_SKIP_WARMUP") == "1": | |
| return | |
| import threading | |
| def _kerja(): | |
| try: | |
| warmup_model() | |
| except ModuleNotFoundError: | |
| print("[oneiros] warm-up: llama-cpp belum terpasang (fase boot?)") | |
| except FileNotFoundError as e: | |
| print(f"[oneiros] warm-up: model belum ada — {e}") | |
| except Exception as e: | |
| print(f"[oneiros] warm-up gagal: {type(e).__name__}: {e}") | |
| threading.Thread(target=_kerja, daemon=True, name="oneiros-warmup").start() | |
| _jalankan_warmup_space() | |
| if __name__ == "__main__": | |
| warmup_model() | |
| demo.launch() | |