YahBible / code /yahbible_main.py
OhBeOneKeyNoBe's picture
Fetch the front-door modules: add them to CODE_FILES and the bootstrap net
71403c6 verified
Raw History Blame Contribute Delete
9.55 kB
#!/usr/bin/env python
r"""YahBible launcher -- the entry point PyInstaller freezes into YahBible.exe.
Out of the box: double-click. On first run it downloads the study library from Hugging Face
(OhBeOneKeyNoBe/YahBible) into a local folder, then starts the local server and opens the app
in your browser. Subsequent runs are instant. Works run-in-place OR installed -- the library
lives in %LOCALAPPDATA%\YahBible so it persists either way.
The library mirrors the app's expected layout under one BASE dir, and YAHBIBLE_BASE points the
server at it (the server falls back to D:\ only on the dev machine).
"""
import os
import sys
import threading
import time
import webbrowser
REPO = "OhBeOneKeyNoBe/YahBible"
PORT = 41537
# study library: HF path -> subpath under BASE (mirrors the app's expected tree)
LIB = {
"data/watchman.db": "watchman/watchman.db",
"data/reflected_red_basic.sqlite": "Holorites_data/daeos/reflected_red_basic.sqlite",
"data/reflected_red.sqlite": "Holorites_data/reflected_red.sqlite",
"data/taviel_versions.sqlite": "Holorites_data/daeos/taviel_versions.sqlite",
"data/taviel_corpus.sqlite": "Holorites_data/daeos/taviel_corpus.sqlite",
"data/english_dict.sqlite": "Holorites_data/daeos/english_dict.sqlite",
"data/piper/en_US-lessac-medium.onnx": "Holorites_data/daeos/piper/en_US-lessac-medium.onnx",
"data/piper/en_US-lessac-medium.onnx.json": "Holorites_data/daeos/piper/en_US-lessac-medium.onnx.json",
"data/reflected_vocal.sqlite": "Holorites/torus_upgrades/reflected_vocal.sqlite",
"data/elaniel_yt.sqlite": "Holorites_data/daeos/elaniel_yt.sqlite",
"data/taviel_redletter.sqlite": "Holorites_data/daeos/taviel_redletter.sqlite",
"data/taviel_doctrine.sqlite": "Holorites_data/daeos/taviel_doctrine.sqlite",
}
# the big lexicon (interlinear + Strong's + dictionary). Optional first-run: it is 70GB, so we
# fetch it in the background after the app is already usable, unless YAHBIBLE_SKIP_LEX is set.
BIG = "data/reflected_red.sqlite"
# --- CODE auto-update -------------------------------------------------------
# The app CODE (server + UI + study data) lives in the HF repo under code/ and is pulled on every
# launch so fixes reach users WITHOUT shipping a new exe. Downloaded modules are loaded over the
# frozen copies; any failure falls back to the frozen build, so the app always starts.
CODE_FILES = ["o_taviel_ui.py", "o_taviel_server.py",
"taviel_commandments.json", "taviel_repentance.json", "taviel_news.json",
# the Gauntlet: served by o_taviel_server, so installed desktops pull these too
"taviel_apologetics.py", "taviel_reason.py", "gauntlet_kb.json",
"taviel_doctrine.py",
# THE JEV FRONT DOOR. taviel_reason.serve() routes through these
# when the decision server and the embedder are both reachable,
# answering identity, vetted, verse, clarify and decline with no
# generated tokens at all. Uploading them to the repo was not
# enough: this list is what an installed desktop actually
# fetches, so a module missing from here is a module that never
# arrives, and the front door would sit in the repo doing
# nothing. They are SAFE to ship to a desktop with no fork
# binary -- serve() gates on a cheap probe and falls back to the
# verbatim gauntlet exactly as before.
"taviel_frontdoor.py", "taviel_decide.py", "taviel_person.py",
"taviel_contrast.py", "taviel_verify.py",
# completed interlinear for verses the lexicon lacks (KJV<->MT versification
# divergences + NT doxologies) -- so installed desktops match web/mobile
"canon_fill.sqlite",
# BlackBox split-key auth: o_taviel_server imports these to publish the two
# encrypted credential halves (HF + GitHub) on signup / password change
"yahbible_auth.py", "auth_sync.py"]
def ensure_code(base):
cdir = os.path.join(base, "code")
os.makedirs(cdir, exist_ok=True)
try:
from huggingface_hub import hf_hub_download
except Exception:
return cdir
import shutil
for fn in CODE_FILES:
for _ in range(3):
try:
cached = hf_hub_download(repo_id=REPO, filename="code/" + fn, repo_type="model")
tmp = os.path.join(cdir, fn + ".tmp")
shutil.copy2(cached, tmp)
os.replace(tmp, os.path.join(cdir, fn))
break
except Exception as e:
print(" code sync skip %s (%s)" % (fn, str(e)[:40]), flush=True)
time.sleep(2)
return cdir
def load_updated_code(cdir):
"""Load the freshly-downloaded o_taviel_ui / o_taviel_server over the frozen copies."""
import importlib.util
for mod in ("o_taviel_ui", "o_taviel_server"):
p = os.path.join(cdir, mod + ".py")
if not (os.path.exists(p) and os.path.getsize(p) > 0):
continue
try:
spec = importlib.util.spec_from_file_location(mod, p)
m = importlib.util.module_from_spec(spec)
sys.modules[mod] = m
spec.loader.exec_module(m)
print(" updated %s loaded from repo" % mod, flush=True)
except Exception as e:
print(" updated %s failed, using built-in (%s)" % (mod, str(e)[:60]), flush=True)
sys.modules.pop(mod, None)
def base_dir():
b = os.environ.get("YAHBIBLE_BASE")
if b:
return b
root = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
b = os.path.join(root, "YahBible", "library")
os.makedirs(b, exist_ok=True)
return b
def have(base, sub):
p = os.path.join(base, sub.replace("/", os.sep))
return os.path.exists(p) and os.path.getsize(p) > 0
def fetch(base, hf_path, sub):
from huggingface_hub import hf_hub_download
dst = os.path.join(base, sub.replace("/", os.sep))
os.makedirs(os.path.dirname(dst), exist_ok=True)
print(" downloading %s ..." % sub, flush=True)
cached = hf_hub_download(repo_id=REPO, filename=hf_path, repo_type="model")
# copy out of the HF cache into our mirrored layout (hardlink if possible)
import shutil
try:
if os.path.exists(dst):
os.remove(dst)
os.link(cached, dst)
except Exception:
shutil.copy2(cached, dst)
def ensure_library(base):
small = {k: v for k, v in LIB.items() if k != BIG}
missing = [(k, v) for k, v in small.items() if not have(base, v)]
if missing:
print("First run: fetching the YahBible study library (~1.2 GB) ...", flush=True)
for k, v in missing:
for _ in range(4):
try:
fetch(base, k, v)
break
except Exception as e:
print(" retry (%s)" % str(e)[:60], flush=True)
time.sleep(4)
# the 70GB lexicon: fetch in the background so the app is usable immediately
if not have(base, LIB[BIG]) and not os.environ.get("YAHBIBLE_SKIP_LEX"):
def _bg():
for _ in range(6):
try:
print("Background: fetching the full lexicon (interlinear + Strong's, ~70 GB)...", flush=True)
fetch(base, BIG, LIB[BIG])
print("Lexicon ready -- interlinear + Strong's now enabled.", flush=True)
break
except Exception as e:
print(" lexicon retry (%s)" % str(e)[:60], flush=True)
time.sleep(10)
threading.Thread(target=_bg, daemon=True).start()
def main():
# when frozen, our bundled modules sit next to the exe / in the PyInstaller temp
here = os.path.dirname(os.path.abspath(sys.argv[0]))
if getattr(sys, "_MEIPASS", None):
here = sys._MEIPASS
if here not in sys.path:
sys.path.insert(0, here)
base = base_dir()
os.environ["YAHBIBLE_BASE"] = base
print("YahBible -- library at:", base, flush=True)
ensure_library(base)
# pull the latest app code from the repo so fixes apply without a new exe
try:
cdir = ensure_code(base)
os.environ["YAHBIBLE_CODE"] = cdir # so the server's _res() finds updated study data
if cdir not in sys.path:
sys.path.insert(0, cdir)
load_updated_code(cdir)
except Exception as e:
print(" code auto-update skipped (%s)" % str(e)[:60], flush=True)
# start the server (import after YAHBIBLE_BASE is set so its path constants resolve)
import o_taviel_server as S
threading.Thread(target=S.main, daemon=True).start()
# wait for it to bind, then open the browser
import socket
for _ in range(120):
s = socket.socket(); s.settimeout(0.4)
try:
s.connect(("127.0.0.1", PORT)); s.close(); break
except Exception:
time.sleep(0.5)
webbrowser.open("http://127.0.0.1:%d/" % PORT)
print("YahBible is running at http://127.0.0.1:%d/ (close this window to quit)" % PORT, flush=True)
try:
while True:
time.sleep(3600)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()