""" # 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()