virtual-characters / scripts /check_modal_connectivity.py
ShadowInk's picture
Upload complete Space runtime files
6bcddd0 verified
Raw
History Blame
5.08 kB
import argparse
import json
import os
import sys
from pathlib import Path
from urllib.parse import urlparse
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
def require_modal():
try:
import modal # noqa: F401
except Exception as exc:
raise SystemExit(
"Modal Python package is not installed. Install with `python -m pip install -r requirements.txt`, "
"then run `modal setup` to log in."
) from exc
def check_remote_methods(service: str = "all") -> int:
require_modal()
from modal_apps.modal_image import CharacterImage, app as image_app
from modal_apps.modal_llm import PersonaLLM, app as llm_app
from modal_apps.modal_tts import CharacterTTS, app as tts_app
all_checks = [
("llm", llm_app, lambda: PersonaLLM().health.remote()),
("tts", tts_app, lambda: CharacterTTS().health.remote()),
("image", image_app, lambda: CharacterImage().health.remote()),
]
checks = [check for check in all_checks if service in {"all", check[0]}]
ok = True
for name, modal_app, fn in checks:
try:
print(f"[{name}] checking health...")
with modal_app.run():
result = fn()
print(json.dumps(result, ensure_ascii=False, indent=2))
except Exception as exc:
ok = False
print(f"[{name}] failed: {exc}", file=sys.stderr)
return 0 if ok else 1
def check_deployed_endpoints() -> int:
try:
import httpx
except Exception as exc:
raise SystemExit("httpx is not installed. Run `python -m pip install -r requirements.txt`.") from exc
llm_url = os.environ.get("VC_MODAL_LLM_URL")
tts_url = os.environ.get("VC_MODAL_TTS_URL")
image_url = os.environ.get("VC_MODAL_IMAGE_URL")
ok = True
if llm_url:
print("[llm:endpoint] checking SSE...")
with httpx.stream(
"POST",
llm_url,
json={"text": "你好,请简单回复一句。", "character": {"display_name": "星萤"}, "max_new_tokens": 40},
timeout=120,
) as response:
response.raise_for_status()
seen = 0
for line in response.iter_lines():
if line:
print(line)
seen += 1
if seen >= 5:
break
else:
print("[llm:endpoint] skipped; set VC_MODAL_LLM_URL")
if tts_url:
print("[tts:endpoint] checking audio...")
response = httpx.post(
_tts_endpoint_url(tts_url),
json={"text": "你好,我在听。", "voice_id": "default", "emotion": "neutral"},
timeout=120,
trust_env=False,
)
response.raise_for_status()
out = Path("modal_tts_check.wav")
out.write_bytes(response.content)
print(f"wrote {out} ({len(response.content)} bytes)")
else:
print("[tts:endpoint] skipped; set VC_MODAL_TTS_URL")
if image_url:
print("[image:endpoint] checking png...")
response = httpx.post(
image_url,
json={"prompt": "original anime virtual character portrait, gentle sci-fi style", "steps": 4, "seed": 7},
timeout=180,
)
response.raise_for_status()
out = Path("modal_image_check.png")
out.write_bytes(response.content)
print(f"wrote {out} ({len(response.content)} bytes)")
else:
print("[image:endpoint] skipped; set VC_MODAL_IMAGE_URL")
return 0 if ok else 1
def _health_targets(url: str) -> list[str]:
base = url.rstrip("/")
tail = base.rsplit("/", 1)[-1]
service_base = base.rsplit("/", 1)[0] if tail in {"tts", "persona_events"} else base
return list(dict.fromkeys([service_base + "/health", service_base + "/health_http", base + "/health"]))
def _tts_endpoint_url(url: str) -> str:
base = url.rstrip("/")
parsed = urlparse(base)
if not parsed.path or parsed.path == "/":
return base
if parsed.path.rstrip("/").rsplit("/", 1)[-1] == "tts":
return base
return base + "/tts"
def main() -> int:
parser = argparse.ArgumentParser(description="Check Modal model connectivity for Virtual Characters.")
parser.add_argument(
"--mode",
choices=["remote-methods", "endpoints"],
default="remote-methods",
help="remote-methods checks Modal class methods; endpoints checks deployed web URLs.",
)
parser.add_argument(
"--service",
choices=["all", "llm", "tts", "image"],
default="all",
help="Limit remote-methods checks to one service.",
)
args = parser.parse_args()
if args.mode == "remote-methods":
return check_remote_methods(args.service)
if args.service != "all":
raise SystemExit("--service is only supported with --mode remote-methods")
return check_deployed_endpoints()
if __name__ == "__main__":
raise SystemExit(main())