Modal
modal
¶
Defines the shared Modal deployment lifecycle used across Mirumoji
Both Modal integrations reuse these helpers but keep their app definitions with their owner. Only the shared, app-agnostic lifecycle functions lives here
Server
The server auto-deploys its GPU-offload app (see server/modal_processing)
which runs only transcription and conversion on modal while everything else
runs locally
Launcher
-
The launcher deploys mirumoji itself as a hosted app, with both backend and frontend running on Modal
-
Its launcher-only host operations (credentials, volume management, URLs, and logs) live in
launcher.modal.lifecycle
Authentication
Every call authenticates through the environment's MODAL_TOKEN_ID /
MODAL_TOKEN_SECRET variables, so managing apps on the user's
own workspace needs nothing but those two tokens
Ownership
Apps deployed through ensure_deployed are tagged managed-by=mirumoji
with the deploying package version, so a running install can recognise its
own app, keep it current, and never create a duplicate
Import Cost
-
modalis only imported lazily inside each function so that importing this module (which thelauncherdoes on every command) stays cheap -
modalis a core dependency of the package so importing it here would be safe otherwise
MANAGED_BY_KEY = 'managed-by'
module-attribute
¶
Tag key marking a Modal app as managed by Mirumoji
MANAGED_BY_VALUE = 'mirumoji'
module-attribute
¶
Tag value marking a Modal app as managed by Mirumoji
NO_WINDOW = getattr(subprocess, 'CREATE_NO_WINDOW', 0)
module-attribute
¶
Suppresses the transient console window a console-less GUI process would
otherwise flash when spawning the modal CLI on Windows (a no-op elsewhere),
matching launcher.core.process
VERSION_KEY = 'mirumoji-version'
module-attribute
¶
Tag key recording the package version a Modal app was deployed from
clean_subprocess_error(stderr)
¶
Distils the modal CLI's noisy error output down to one readable line
The modal CLI frames errors with Rich, so its stderr carries ANSI
colour codes and box-drawing borders. This strips both and returns the last
line that still has content, which is where the CLI puts the actual error
ASCII Borders
Rich falls back to +, -, and | borders on a non-UTF terminal
(a Windows cp1252 console), so those are stripped too. Otherwise a
border line would survive and be returned as the error
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stderr
|
str
|
The captured subprocess stderr |
required |
Returns:
| Type | Description |
|---|---|
str
|
The concise error line, or a generic fallback when none is found |
deployed_tags(name)
¶
Returns the tags of a deployed Modalapp, orNone` when it is not
deployed
Untagged Apps
-
A deployed app whose tags cannot be read is reported as an empty mapping rather than
None -
This way, callers can tell a non-tagged app apart from one that doesn't exist
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The deployed app name |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str] | None
|
The app's tag mapping, |
Raises:
| Type | Description |
|---|---|
ModalError
|
If the workspace cannot be reached to look the app up |
ensure_authenticated()
¶
Ensures that the Modal API credentials are present in the environment
Raises:
| Type | Description |
|---|---|
ModalError
|
If either |
ensure_deployed(app, name, *, version, extra_tags=None, force=False)
¶
Deploys a Modal app under name unless an app of the same name whose
tags already match managed_tags(version) and any extra_tags is deployed
Idempotent + Tracked
-
Looks the app up first and returns early when a Mirumoji-managed app of the same version (and matching
extra_tags) is already deployed -
extra_tagsare part of the deployed identity: they are compared alongside the version and set on deploy, so a change to them (not only to the version) forces a redeploy. The host uses this so a changed host mode or reservation is never mistaken for the live app -
If there's no app deployed under
name, or that app doesn't have themanaged_tags(__version__)tags, the app is deployed with those tags set -
Redeploys happen in place (Modal updates an app of the same
namerather than creating a duplicate), so a package upgrade transparently rolls the deployed app forward
Force
-
When
force=True, the app is always redeployed -
This is needed to roll out a code or image change without a version bump during development
Blocking
Performs network I/O, so a caller on the event loop should run it in a thread
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
App
|
The locally-defined app to deploy |
required |
name
|
str
|
The deployed app name |
required |
version
|
str
|
The package version being deployed |
required |
extra_tags
|
dict[str, str] | None
|
Extra identity tags compared and set alongside the managed tags, so a deploy whose configuration changed (not only its version) is not mistaken for the live one |
None
|
force
|
bool
|
Redeploy even when the same version is already live |
False
|
Raises:
| Type | Description |
|---|---|
ModalError
|
If credentials are missing or the deploy fails |
managed_tags(version)
¶
modal_cli_argv(*args)
¶
Builds the argv for a modal CLI invocation
Prefers <interpreter> -m modal, falling back to a modal entry point on
the PATH when the process has no interpreter to spawn
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
str
|
The |
()
|
Returns:
| Type | Description |
|---|---|
list[str]
|
The argv to spawn |
Raises:
| Type | Description |
|---|---|
ModalError
|
If neither an interpreter nor a |
python_executable()
¶
Returns a real Python interpreter to spawn, or None when there is none
Packaged Builds
-
A
pip-installed launcher runs under a genuine interpreter, sosys.executableis used directly -
Inside a packaged bundle
sys.executableis the app binary, soPATHis searched instead andNoneis returned when the machine has no interpreter at all
Store Alias Stub
On Windows, PATH carries a fake python.exe under
Microsoft\WindowsApps (the app execution alias). Running it only
prints a Microsoft Store install prompt, so a hit from there is not
an interpreter and is skipped
Returns:
| Type | Description |
|---|---|
str | None
|
A path to a Python interpreter, or |
stop(name)
¶
Stops a deployed Modal app under name, removing it from the workspace
In-Process First
-
Modal's Python SDK exposes no public call to stop an app, so this drives modal's own command callback in-process and falls back to the
modal app stopCLI when that is unavailable -
The packaged desktop GUI embeds Python in the app binary, so
sys.executableis not an interpreter there. Running the callback in-process keeps the stop working without spawning anything -
Stopping is one-way, so a stopped app is redeployed rather than restarted
Best Effort
-
Never raises, so a server caller (shutdown) can ignore the return
-
The failure reason is both logged and returned, so a CLI / GUI caller can surface it to the user instead of reporting a false success
Blocking
Performs network I/O, so a caller on the event loop should run it in a thread
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The deployed app name to stop |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
A short error message when the stop failed, or |