add modal endpoint and magpietts
Browse files- README.md +9 -0
- app.py +9 -0
- backend/modal_client.py +16 -0
- tests/test_app_startup.py +17 -0
- tests/test_modal_client.py +17 -0
README.md
CHANGED
|
@@ -12,3 +12,12 @@ short_description: Convert your library to the spoken word
|
|
| 12 |
---
|
| 13 |
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 15 |
+
|
| 16 |
+
## Modal backend configuration
|
| 17 |
+
|
| 18 |
+
Set these as Hugging Face Space secrets if you want to use the optional Modal backend:
|
| 19 |
+
|
| 20 |
+
- `SCRIPTORIUM_MODAL_BASE_URL`
|
| 21 |
+
- `SCRIPTORIUM_MODAL_AUTH_TOKEN`
|
| 22 |
+
|
| 23 |
+
`SCRIPTORIUM_MODAL_BASE_URL` must be the deployed `modal.run` web endpoint, not the Modal dashboard URL under `modal.com/apps/...`.
|
app.py
CHANGED
|
@@ -39,6 +39,15 @@ store = SessionStore(root=TEMP_ROOT, ttl_seconds=SESSION_TTL_SECONDS)
|
|
| 39 |
synthesis_service = SynthesisService(session_root=TEMP_ROOT)
|
| 40 |
|
| 41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
def _session_root(session_id: str) -> Path:
|
| 43 |
return store.ensure_session(session_id).root
|
| 44 |
|
|
|
|
| 39 |
synthesis_service = SynthesisService(session_root=TEMP_ROOT)
|
| 40 |
|
| 41 |
|
| 42 |
+
def _warn_about_modal_configuration() -> None:
|
| 43 |
+
warning = synthesis_service.modal_client.configuration_warning()
|
| 44 |
+
if warning:
|
| 45 |
+
print(f"Modal configuration warning: {warning}")
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
_warn_about_modal_configuration()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
def _session_root(session_id: str) -> Path:
|
| 52 |
return store.ensure_session(session_id).root
|
| 53 |
|
backend/modal_client.py
CHANGED
|
@@ -3,6 +3,7 @@ import os
|
|
| 3 |
import time
|
| 4 |
from pathlib import Path
|
| 5 |
from typing import Dict, Iterable, List, Optional
|
|
|
|
| 6 |
|
| 7 |
import requests
|
| 8 |
|
|
@@ -36,6 +37,21 @@ class ModalSynthesisClient:
|
|
| 36 |
def is_configured(self) -> bool:
|
| 37 |
return bool(self.base_url)
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
def generate_preview(
|
| 40 |
self,
|
| 41 |
*,
|
|
|
|
| 3 |
import time
|
| 4 |
from pathlib import Path
|
| 5 |
from typing import Dict, Iterable, List, Optional
|
| 6 |
+
from urllib.parse import urlparse
|
| 7 |
|
| 8 |
import requests
|
| 9 |
|
|
|
|
| 37 |
def is_configured(self) -> bool:
|
| 38 |
return bool(self.base_url)
|
| 39 |
|
| 40 |
+
def configuration_warning(self) -> Optional[str]:
|
| 41 |
+
if not self.base_url:
|
| 42 |
+
return None
|
| 43 |
+
|
| 44 |
+
parsed = urlparse(self.base_url)
|
| 45 |
+
host = (parsed.netloc or "").lower()
|
| 46 |
+
path = parsed.path or ""
|
| 47 |
+
if host == "modal.com" and path.startswith("/apps/"):
|
| 48 |
+
return (
|
| 49 |
+
"SCRIPTORIUM_MODAL_BASE_URL points to a Modal dashboard URL, not a callable web endpoint. "
|
| 50 |
+
"Use the deployed modal.run base URL instead, for example "
|
| 51 |
+
"'https://<your-app>.modal.run'."
|
| 52 |
+
)
|
| 53 |
+
return None
|
| 54 |
+
|
| 55 |
def generate_preview(
|
| 56 |
self,
|
| 57 |
*,
|
tests/test_app_startup.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import importlib
|
| 2 |
import sys
|
|
|
|
| 3 |
|
| 4 |
import gradio as gr
|
| 5 |
|
|
@@ -31,3 +32,19 @@ def test_launch_app_disables_ssr_for_custom_server_mode(monkeypatch) -> None:
|
|
| 31 |
assert captured["server_port"] == 7860
|
| 32 |
assert captured["ssr_mode"] is False
|
| 33 |
assert captured["_app"] is module.app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import importlib
|
| 2 |
import sys
|
| 3 |
+
from types import SimpleNamespace
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
|
|
|
|
| 32 |
assert captured["server_port"] == 7860
|
| 33 |
assert captured["ssr_mode"] is False
|
| 34 |
assert captured["_app"] is module.app
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def test_app_import_warns_for_modal_dashboard_url(monkeypatch) -> None:
|
| 38 |
+
sys.modules.pop("app", None)
|
| 39 |
+
printed = []
|
| 40 |
+
|
| 41 |
+
monkeypatch.setenv(
|
| 42 |
+
"SCRIPTORIUM_MODAL_BASE_URL",
|
| 43 |
+
"https://modal.com/apps/mattkevan/main/deployed/scriptorium-tts",
|
| 44 |
+
)
|
| 45 |
+
monkeypatch.setattr("builtins.print", lambda *args, **kwargs: printed.append(" ".join(str(arg) for arg in args)))
|
| 46 |
+
|
| 47 |
+
importlib.import_module("app")
|
| 48 |
+
|
| 49 |
+
assert any("SCRIPTORIUM_MODAL_BASE_URL" in line for line in printed)
|
| 50 |
+
assert any("modal.run" in line for line in printed)
|
tests/test_modal_client.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.modal_client import ModalSynthesisClient
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_modal_client_warns_when_configured_with_dashboard_url() -> None:
|
| 5 |
+
client = ModalSynthesisClient(base_url="https://modal.com/apps/mattkevan/main/deployed/scriptorium-tts")
|
| 6 |
+
|
| 7 |
+
warning = client.configuration_warning()
|
| 8 |
+
|
| 9 |
+
assert warning is not None
|
| 10 |
+
assert "modal.run" in warning
|
| 11 |
+
assert "dashboard" in warning.lower()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_modal_client_accepts_modal_run_base_url() -> None:
|
| 15 |
+
client = ModalSynthesisClient(base_url="https://scriptorium-tts--mattkevan.modal.run")
|
| 16 |
+
|
| 17 |
+
assert client.configuration_warning() is None
|