| """Root entry point β a thin shim over the FISHBOWL Gradio shell. |
| |
| The app itself lives in ``src.ui.fishbowl.app`` (Unit 9). This shim keeps ``uv run |
| app.py`` working and preserves the no-API-key / offline behaviour: the deterministic |
| stub drives the cast so the demo is reproducible on stage with no credentials. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import os |
| import sys |
| from pathlib import Path |
|
|
|
|
| def _load_dotenv(filename: str = ".env") -> None: |
| """Load ``KEY=VALUE`` pairs from a sibling ``.env`` into the environment, if present. |
| |
| Real environment variables win (``setdefault``) β this only fills gaps, never |
| overrides what the shell or CI already set. Parsed in Python, not the shell, so |
| values with shell-special characters load correctly: a Neon ``DATABASE_URL``'s |
| ``&``, a JSON ``MEMORY_INDEX_CONFIG`` β all of which silently abort ``source .env``. |
| Absent ``.env`` β no-op, preserving the offline-by-default behaviour (no keys, stub). |
| """ |
| path = Path(__file__).resolve().parent / filename |
| if not path.exists(): |
| return |
| for raw in path.read_text(encoding="utf-8").splitlines(): |
| line = raw.strip() |
| if not line or line.startswith("#"): |
| continue |
| if line.startswith("export "): |
| line = line[len("export ") :] |
| key, sep, value = line.partition("=") |
| if not sep: |
| continue |
| key, value = key.strip(), value.strip() |
| if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'": |
| value = value[1:-1] |
| |
| |
| |
| if key and value: |
| os.environ.setdefault(key, value) |
|
|
|
|
| |
| |
| |
| if "pytest" not in sys.modules: |
| _load_dotenv() |
| |
| |
| |
| from src import observability as _obs |
|
|
| _obs.configure() |
|
|
| from src.ui.fishbowl.app import demo, launch |
|
|
| __all__ = ["demo", "launch", "_load_dotenv"] |
|
|
| if __name__ == "__main__": |
| launch() |
|
|