chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# UI Preview
|
||||
|
||||
Deploy a live, per-PR preview of the Omnigent web UI as a
|
||||
[Databricks App](https://docs.databricks.com/aws/en/dev-tools/databricks-apps/)
|
||||
when a PR changes the frontend (`web/`).
|
||||
|
||||
## How it works
|
||||
|
||||
1. A maintainer adds the `ui-preview` label to a PR (the workflow is gated to
|
||||
`OWNER`/`MEMBER`/`COLLABORATOR` authors).
|
||||
2. The [UI Preview workflow](../workflows/ui-preview.yml) builds the SPA + the
|
||||
Omnigent wheels and deploys them to an ephemeral Databricks App
|
||||
(`omnigent-ui-preview-pr-<N>`).
|
||||
3. A comment with the preview URL is posted on the PR and updated on each push.
|
||||
4. The app is deleted automatically when the PR is closed.
|
||||
|
||||
## What it is
|
||||
|
||||
Unlike Omnigent's production Databricks deploy (`deploy/databricks/`, backed by
|
||||
Lakebase Postgres + UC Volumes), the preview is intentionally ephemeral and
|
||||
self-contained: a **SQLite** database + local-disk artifact store, thrown away
|
||||
on teardown.
|
||||
|
||||
There is **no LLM or runner baked into the preview** -- Omnigent runs agent
|
||||
turns on a runner the user connects from their own machine or sandbox
|
||||
(`omnigent run … --server <preview-url>`), where the model credentials live. So
|
||||
the preview is for reviewing the UI's look-and-feel and navigation; to drive a
|
||||
real session, connect your own host to the preview URL.
|
||||
|
||||
## Access
|
||||
|
||||
Preview apps are only accessible to maintainers with Databricks workspace
|
||||
access (the Apps proxy injects `X-Forwarded-Email`, so the app runs in header
|
||||
auth mode).
|
||||
|
||||
## Setup (one-time, by a maintainer)
|
||||
|
||||
Add these repo secrets:
|
||||
|
||||
- `DATABRICKS_HOST`
|
||||
- `DATABRICKS_CLIENT_ID`
|
||||
- `DATABRICKS_CLIENT_SECRET`
|
||||
|
||||
Create a `ui-preview` label. If the workspace IP-allowlists, register a
|
||||
static-IP runner and point the `deploy`/`cleanup` jobs at it.
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Entry point for the per-PR UI Preview app (Databricks Apps).
|
||||
|
||||
Unlike Omnigent's production Databricks deploy (``deploy/databricks/``, which
|
||||
uses Lakebase Postgres + UC Volumes), this preview is deliberately *ephemeral
|
||||
and self-contained* so a fresh app can be created and torn down per PR with no
|
||||
external state: a SQLite database + local-disk artifact store under a temp dir.
|
||||
|
||||
There is no bundled LLM or runner. Omnigent executes agent turns on a runner
|
||||
that the user connects from their own machine/sandbox (``omnigent run … --server
|
||||
<url>``), so the preview only needs to serve the web UI + API. A reviewer browses
|
||||
the UI as-is, and can connect their own host to drive a real session.
|
||||
|
||||
The prebuilt web SPA is shipped separately as ``build.tar.gz`` (keeping the
|
||||
wheel small) and extracted into the installed ``omnigent`` package so the server
|
||||
mounts it at ``/``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
logging.basicConfig(level=logging.INFO, stream=sys.stderr, force=True)
|
||||
logger = logging.getLogger("omnigent-ui-preview")
|
||||
|
||||
HERE = Path(__file__).parent.resolve()
|
||||
# Databricks Apps expects the app to listen on DATABRICKS_APP_PORT (8000 by
|
||||
# convention); fall back to 8000 for local runs of this script.
|
||||
PORT = int(os.environ.get("DATABRICKS_APP_PORT", "8000"))
|
||||
WORK_DIR = Path(os.environ.get("OMNIGENT_PREVIEW_WORKDIR", "/tmp/omnigent-preview"))
|
||||
DB_PATH = WORK_DIR / "omnigent.db"
|
||||
ARTIFACT_DIR = WORK_DIR / "artifacts"
|
||||
|
||||
|
||||
def _extract_spa() -> None:
|
||||
"""Extract the prebuilt SPA into the installed omnigent package.
|
||||
|
||||
The build job ships ``build.tar.gz`` (containing a ``web-ui`` dir) next to
|
||||
this file; the server serves ``omnigent/server/static/web-ui`` at ``/``.
|
||||
"""
|
||||
tar_path = HERE / "build.tar.gz"
|
||||
if not tar_path.is_file():
|
||||
logger.warning("No build.tar.gz found at %s -- UI will be API-only", tar_path)
|
||||
return
|
||||
import omnigent.server
|
||||
|
||||
target = Path(omnigent.server.__file__).parent / "static"
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
logger.info("Extracting SPA from %s into %s", tar_path, target)
|
||||
with tarfile.open(tar_path) as tar:
|
||||
# filter="data" rejects path-traversal / unsafe members; the tarball is
|
||||
# built from fork-supplied UI output, and this is the 3.14 default.
|
||||
tar.extractall(target, filter="data")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
WORK_DIR.mkdir(parents=True, exist_ok=True)
|
||||
ARTIFACT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
_extract_spa()
|
||||
|
||||
# The Databricks Apps proxy injects X-Forwarded-Email on every request, so
|
||||
# run in header auth mode (matches deploy/databricks/src/app.py) -- no login
|
||||
# page, and the proxy is the trust boundary.
|
||||
os.environ.setdefault("OMNIGENT_AUTH_PROVIDER", "header")
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"omnigent.cli",
|
||||
"server",
|
||||
"--host",
|
||||
"0.0.0.0",
|
||||
"--port",
|
||||
str(PORT),
|
||||
"--database-uri",
|
||||
f"sqlite:///{DB_PATH}",
|
||||
"--artifact-location",
|
||||
str(ARTIFACT_DIR),
|
||||
"--no-open",
|
||||
]
|
||||
logger.info("Starting Omnigent server: %s", " ".join(cmd))
|
||||
os.execvp(cmd[0], cmd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
command: ["python", "app.py"]
|
||||
Reference in New Issue
Block a user