Skip to content

Add first-class GTSFM Studio workflow - #1121

Open
suhanpark wants to merge 18 commits into
masterfrom
studio/package-cli
Open

Add first-class GTSFM Studio workflow#1121
suhanpark wants to merge 18 commits into
masterfrom
studio/package-cli

Conversation

@suhanpark

Copy link
Copy Markdown
Collaborator

Summary

Introduce a packaged, browser-based GTSFM Studio that launches with gtsfm run and provides a FastAPI-backed React workspace for configuring, running, monitoring, and inspecting reconstructions.

Why

The existing workflow requires users to know repository paths and command-line arguments. This creates a first-class package entry point and a discoverable UI while retaining the terminal execution path.

User impact

  • Launch the local workspace with gtsfm run
  • Choose datasets, reconstruction models, splat implementations, and detected hardware in the UI
  • Upload images or select maintained sample data
  • Monitor jobs and inspect/download reconstruction results in the integrated 3D viewer
  • Use VGGT as the default reconstruction model

Validation

  • python -m pytest tests/visualization/test_runtime.py tests/ui/test_optional_process_graph.py -q (36 passed)
  • pnpm build

Review notes

This is PR 1 of a stacked draft series. Later drafts isolate the reusable Modal runtime, workspace-control improvements, and live Gaussian visualization.

@akshay-krishnan
akshay-krishnan marked this pull request as ready for review September 1, 2026 15:21
@akshay-krishnan

Copy link
Copy Markdown
Collaborator

@copilot summarize the changes to each file in this PR

Copilot AI commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@copilot summarize the changes to each file in this PR

Here’s a per-file summary of what changed in this PR:

  • .github/workflows/modal-runtime-image.yml: Added a workflow to build/push ghcr.io/borglab/gtsfm-modal-runtime on master, tags, or manual trigger.
  • .gitignore: Added frontend/build ignore entries (node_modules, .pnpm-store, Studio sourcemap) and normalized trailing newline.
  • README.md: Added full “GTSFM Studio” install/usage docs, local+remote workflow guidance, Modal notes, and frontend dev commands.
  • docker/modal-runtime.Dockerfile: Added a CUDA-based dependency/runtime image for Modal with uv-managed Python deps.
  • gtsfm/cli.py: Added packaged gtsfm CLI entrypoint with run, execute, hardware, and viz subcommands.
  • gtsfm/cluster_optimizer/cluster_optimizer_cacher.py: When loading cached reconstructions, now optionally re-runs Gaussian splat render/export tasks.
  • gtsfm/cluster_optimizer/cluster_vggt.py: Added optional gaussian_splatting_optimizer support and GS output/video generation in VGGT flow.
  • gtsfm/configs/vggt.yaml: Added gaussian_splatting_optimizer config slot for --run_gs path.
  • gtsfm/frontend/vggt_geometry_transformer.py: Default VGGT weights now auto-download from Hugging Face on first use.
  • gtsfm/runner.py: Added live Dask stats publishing, --gs_max_steps, generalized GS override wiring, and safer run/shutdown lifecycle.
  • gtsfm/scene_optimizer.py: Made process-graph export best-effort (skip/continue if Graphviz dot is unavailable/fails).
  • gtsfm/splat/gaussian_splatting.py: Publishes periodic live training updates/previews during GS optimization.
  • gtsfm/splat/live.py: Added atomic live-status + live-preview bridge for GS progress.
  • gtsfm/utils/torch.py: Added GTSFM_SELECTED_DEVICE handling with validation for CPU/MPS/CUDA selection.
  • pyproject.toml: Added app/runtime deps (FastAPI/Uvicorn/Modal/certifi), gtsfm console script, packaging rules, and updated dependency source/environment constraints.
  • tests/ui/test_optional_process_graph.py: Added tests verifying process-graph export failures don’t block reconstruction.
  • tests/ui/test_process_graph_generator.py: Graphviz-dependent test now skips cleanly when dot is missing.
  • tests/visualization/__init__.py: Added package marker for visualization tests.
  • tests/visualization/test_runtime.py: Added comprehensive runtime/workspace tests (schema, hardware, uploads, samples, remote, Modal, auth/privacy, live endpoints, cancellation).
  • uv.lock: Regenerated lockfile for dependency and packaging changes.
  • visualization/__init__.py: Added package marker for visualization runtime package.
  • visualization/app.py: Replaced/expanded server as FastAPI app with typed request models, API-key protection, uploads, sample prep, jobs, live state, Modal deploy/discover, and WebSocket streams.
  • visualization/frontend/package.json: Added React+TypeScript+Vite frontend package and scripts (dev, check, build).
  • visualization/frontend/pnpm-lock.yaml: Added frontend dependency lockfile.
  • visualization/frontend/scripts/copy-viewer-vendor.mjs: Added script to copy Babylon vendor bundle into static assets.
  • visualization/frontend/src/main.tsx: Added full Studio React UI (run configuration, setup checks, samples/uploads, local/remote execution controls, activity/results/log/live panels).
  • visualization/frontend/src/styles.css: Added Studio UI stylesheet source.
  • visualization/frontend/tsconfig.json: Added strict TypeScript config.
  • visualization/frontend/vite.config.ts: Added Vite build config for Studio frontend.
  • visualization/modal_app.py: Added Modal ASGI deployment definition for protected remote Studio workspace.
  • visualization/modal_deployment.py: Added local deployment manager to validate resources, run/cancel Modal deploys, track status/logs, and derive scoped workspace API keys.
  • visualization/requirements.txt: Switched visualization server deps from Flask to FastAPI/Uvicorn/multipart stack.
  • visualization/runtime.py: Added runtime core (hardware detection, config schema, setup checks/install helpers, job specs/state management, local/remote execution plumbing).
  • visualization/samples.py: Added curated sample catalog + secure GitHub-backed sample preparation/downloading with certifi CA bundle.
  • visualization/static/brand/bee-favicon.png: Added Studio favicon asset.
  • visualization/static/brand/sfm-logo.png: Added Studio logo asset.
  • visualization/static/studio.css: Added compiled frontend CSS artifact.
  • visualization/static/studio.js: Added compiled frontend JS artifact.
  • visualization/static/style.css: Removed legacy stylesheet replaced by Studio assets.
  • visualization/static/vendor/babylon.js: Added vendored Babylon runtime for local/offline viewer use.
  • visualization/static/viewer.js: Updated viewer visuals/controls (dark theme defaults, camera tuning, ground/grid/axes toggles, overlay theming, frustum rendering updates).
  • visualization/templates/index.html: Switched template to Studio root mount and new static asset loading (studio.css/js, local Babylon vendor, viewer script).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we add a readme in this folder that explains the different workflows? what is this one for?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea added in #1140

Comment thread docker/modal-runtime.Dockerfile Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we add a readme in this folder to explain how this dockerfile is meant to be used?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that the Dockerfile duplicates the dependency setup already defined in modal_app.py. i removed the custom Dockerfile and its publishing path

Comment on lines +157 to +170
delayed(self._save_cached_result_outputs, pure=False)(cached_graph, context.output_paths.results)
]
gaussian_optimizer = vars(self._optimizer).get("gaussian_splatting_optimizer")
if gaussian_optimizer is not None:
import gtsfm.splat.rendering as gtsfm_rendering

images_graph = context.get_delayed_image_map()
splats_graph, cfg_graph = gaussian_optimizer.create_computation_graph(images_graph, cached_graph)
io_tasks.extend(
[
delayed(gtsfm_rendering.save_splats)(context.output_paths.results, splats_graph),
delayed(gtsfm_rendering.generate_interpolated_video)(
images_graph,
cached_graph,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you test running gaussian splatting with the cluster optimizer cacher? this feels unrelated to this PR, not sure if its intentional?

Comment thread gtsfm/cluster_optimizer/cluster_vggt.py Outdated
gtsfm_data = gtsfm_data.filter_landmark_measurements(
pre_ba_max_reproj_error, min_track_length
)
gtsfm_data = gtsfm_data.filter_landmark_measurements(pre_ba_max_reproj_error, min_track_length)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure the maximum line length setting in your IDE has been applied? the formatting seems off here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll check my editor’s formatting settings and clean up the formatting here

Comment thread gtsfm/splat/gaussian_splatting.py Outdated
)

if step == 0 or (step + 1) % 10 == 0 or (step + 1) == max_steps:
from gtsfm.splat.live import publish_training_update

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this file heavy to import? does it need some cuda kernels registered? normally we put all imports at the top unless we really want to avoid importing it for most use cases.

Comment thread gtsfm/runner.py Outdated
from gtsfm.scene_optimizer import SceneOptimizer


def _scheduler_task_counts(dask_scheduler: Any) -> dict[str, int]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid bloating in this central and important file, can we move these functions to a separate file?

Comment thread gtsfm/runner.py Outdated
OmegaConf.update(
main_cfg,
"cluster_optimizer.gaussian_splatting_optimizer",
snapshot_path,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"snapshot" is a bit cryptic. not obvious what this means, until someone reads and understands the code.



class TestProcessGraphGenerator(unittest.TestCase):
@unittest.skipUnless(shutil.which("dot"), "Graphviz 'dot' is not installed")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this not a dependency? why skip these tests?

Comment thread visualization/requirements.txt
suhanpark and others added 15 commits September 10, 2026 13:10
addressing file removals
docker/modal-runtime.Dockerfile
.github/workflows/modal-runtime-image.yml
moved splat live import to top
snapshot_path → gs_optimizer_config_path
Refactor filter_landmark_measurements call for better readability.
Remove test for modal deployment preferring prebuilt runtime.
Removed redundant information about the prebuilt runtime and clarified the setup process for Modal workspace.
Removed redundant cancellation check for deployment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants