How Modly's API Layer Is Structured and How It Interfaces With the FastAPI Backend
Modly's API layer uses a FastAPI application with modular routers, a central GeneratorRegistry for model discovery, and abstract BaseGenerator subclasses that handle inference either in-process or via isolated subprocesses.
Modly is an open-source AI inference framework for 3D generation. Its API layer architecture cleanly separates HTTP concerns from model-specific logic, enabling extensibility through a plugin-based extension system. This article examines the core components and their interactions based on the actual source code in the lightningpixel/modly repository.
Core Components of the API Layer
api/main.py: FastAPI Application Entry Point
The root of the API layer resides in api/main.py. This file instantiates the FastAPI app, configures CORS middleware, registers all routers, and defines an asynchronous lifespan context that manages the full application lifecycle.
# api/main.py
app = FastAPI(
title="Modly API",
version="0.4.1",
lifespan=lifespan,
)
The lifespan context manager is critical: it calls GeneratorRegistry.initialize() on startup to discover and load extensions, then performs graceful shutdown when the application terminates.
Routers are included with optional URL prefixes:
app.include_router(generation.router, prefix="/generate")
app.include_router(status.router)
# … additional routers …
The same file also implements serve_workspace_file, which streams generated assets from WORKSPACE_DIR back to clients.
Modular Routers in api/routers/
Endpoints are organized by domain into separate APIRouter modules:
| Router File | Purpose |
|---|---|
api/routers/generation.py |
Image-to-mesh generation, job tracking, cancellation, progress callbacks |
api/routers/status.py |
Health checks for the Electron frontend |
api/routers/models.py |
Model management operations |
api/routers/settings.py |
Configuration endpoints |
api/routers/extensions.py |
Extension discovery and metadata |
api/routers/export.py |
Export format handling |
api/routers/workflow_runs.py |
Workflow execution |
api/routers/agent.py |
Agent-related operations |
Each router encapsulates related functionality and is imported into api/main.py for registration.
Generator Registry: The Bridge to AI Inference
services/generator_registry.GeneratorRegistry
Located in api/services/generator_registry.py, the GeneratorRegistry is the central coordination point between the HTTP layer and model implementations. It performs three core responsibilities:
- Discovery: Scans
EXTENSIONS_DIRfor valid extensions (folders containingmanifest.json) - Instantiation: Creates either direct
BaseGeneratorinstances orExtensionProcesswrappers based on extension configuration - Lifecycle management: Handles model switching, loading, unloading, and status tracking
Environment variables configure critical paths (lines 24-31 in generator_registry.py):
EXTENSIONS_DIR # Where user extensions are located
WORKSPACE_DIR # Output directory for generated files
MODELS_DIR # Cache for downloaded model checkpoints
services.generators.base.BaseGenerator
All model adapters must inherit from the abstract base class defined in api/services/generators/base.py. The contract requires implementation of:
generate()– Execute inference and write output to workspaceload()– Initialize model weights and move to target deviceunload()– Free GPU memory and resourcesis_loaded()– Check load stateis_downloaded()– Verify model weights are cached locally
services.extension_process.ExtensionProcess
For extensions shipping with their own virtual environment, api/services/extension_process.py provides a subprocess wrapper that implements the same public interface as BaseGenerator. This allows the rest of the API to treat both execution modes uniformly—whether running in-process or isolated.
Request-Response Flow: End-to-End Interaction
The interface between FastAPI backend and model inference follows a structured pipeline:
-
Application startup – FastAPI invokes the
lifespancontext;GeneratorRegistry.initialize()discovers extensions and prepares the workspace -
Request routing – HTTP requests route to appropriate
APIRouterendpoints; for example,POST /generate/from-imagehitsapi/routers/generation.py -
Model selection – The endpoint validates
model_idand callsgenerator_registry.switch_model(model_id)to activate the requested generator -
Model loading –
generator_registry.get_active()triggers on-demand download (if needed) and loads the model in a background thread with progress callbacks—keeping the route non-blocking -
Generation execution – A background task (
_run_generation) invokes the generator'sgeneratemethod with image bytes, parameters, progress callback, and optional cancellation event; output writes toWORKSPACE_DIR/<collection> -
Result serving – The endpoint returns a job ID for polling; completed results are accessible via
/workspace/<relative-path>served byserve_workspace_file
Adding Custom Model Extensions
The architecture enables hot-pluggable models without modifying core API code:
-
Create extension folder:
/path/to/extensions/my_model/ -
Add
manifest.json:
{
"id": "my_model",
"name": "My Model",
"generator_class": "MyGenerator",
"type": "model",
"nodes": []
}
-
Implement
generator.pysubclassingBaseGenerator -
Set
EXTENSIONS_DIR=/path/to/extensionsand restart Modly
The GeneratorRegistry automatically discovers and registers the extension on startup.
Calling the Generation API
POST /generate/from-image HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="image"; filename="example.png"
Content-Type: image/png
<binary PNG data>
------WebKitFormBoundary
Content-Disposition: form-data; name="model_id"
sf3d
------WebKitFormBoundary
Content-Disposition: form-data; name="params"
{"prompt":"A futuristic cityscape"}
------WebKitFormBoundary--
The response contains a job_id. Poll GET /generate/status/{job_id} until status is "done", then retrieve the result at the provided output_url.
Summary
api/main.pyinstantiates FastAPI, configures middleware, registers routers, and manages application lifespanAPIRoutermodules inapi/routers/group endpoints by domain with optional URL prefixesGeneratorRegistrydiscovers extensions, manages model lifecycle, and abstracts execution mode (direct or subprocess)BaseGeneratordefines the contract all model adapters must implementExtensionProcessprovides isolation for extensions with custom virtual environments- Background task execution keeps HTTP routes responsive during model loading and generation
- Plugin-based extension system allows adding new models by dropping folders into
EXTENSIONS_DIR
Frequently Asked Questions
What file serves as the FastAPI application entry point in Modly?
api/main.py serves as the entry point. It creates the FastAPI instance with lifespan context management, adds CORS middleware, includes all routers with their prefixes, and implements workspace file serving.
How does Modly isolate extensions that require different Python dependencies?
Modly uses ExtensionProcess in api/services/extension_process.py to wrap such extensions. This class runs the generator in a separate subprocess with its own virtual environment while exposing the same interface as direct BaseGenerator instances, allowing uniform treatment by the registry and routers.
Can new AI models be added without modifying Modly's source code?
Yes. Place a folder containing manifest.json and a generator.py implementing BaseGenerator into the directory specified by EXTENSIONS_DIR. The GeneratorRegistry automatically discovers and loads it on the next startup.
How does Modly prevent model loading from blocking HTTP requests?
Both model loading and generation execute in background threads managed through FastAPI's BackgroundTask or asyncio mechanisms. Progress callbacks update job status while the initial HTTP response returns immediately with a job ID for polling.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →