PLUS: мультипроход со слиянием и проверкой нот по аудио
Browse files- plus/serve_muscriptor.py +49 -8
plus/serve_muscriptor.py
CHANGED
|
@@ -90,7 +90,31 @@ def healthy(port: int, timeout: float = 3.0) -> bool:
|
|
| 90 |
return False
|
| 91 |
|
| 92 |
|
| 93 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
command = [sys.executable, "-m", "muscriptor", "serve",
|
| 95 |
"--model", model, "--device", device, "--host", "0.0.0.0", "--port", str(port)]
|
| 96 |
if dtype:
|
|
@@ -98,7 +122,8 @@ def start_server(port: int, model: str, device: str, dtype: str | None) -> subpr
|
|
| 98 |
log = open("muscriptor_server.log", "a")
|
| 99 |
say("запускаю: " + " ".join(command[1:]))
|
| 100 |
process = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT,
|
| 101 |
-
stdin=open(os.devnull, "rb")
|
|
|
|
| 102 |
deadline = time.time() + 600
|
| 103 |
started = time.time()
|
| 104 |
while time.time() < deadline:
|
|
@@ -342,7 +367,8 @@ def ensure_audio_tools() -> bool:
|
|
| 342 |
|
| 343 |
|
| 344 |
def start_proxy(server_port: int, listen_port: int,
|
| 345 |
-
instruments: str = BAND_INSTRUMENTS
|
|
|
|
| 346 |
"""SSE-прокси: держит поток /transcribe живым через медленный туннель."""
|
| 347 |
script = pathlib.Path(__file__).with_name("sse_proxy.py")
|
| 348 |
if not script.exists():
|
|
@@ -350,7 +376,8 @@ def start_proxy(server_port: int, listen_port: int,
|
|
| 350 |
proc = subprocess.Popen(
|
| 351 |
[sys.executable, "-u", str(script),
|
| 352 |
"--listen", str(listen_port), "--target", str(server_port),
|
| 353 |
-
"--instruments", instruments]
|
|
|
|
| 354 |
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
| 355 |
for _ in range(20):
|
| 356 |
time.sleep(0.5)
|
|
@@ -391,8 +418,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 391 |
help="имя пользователя для локального туннеля (по умолчанию nokey)")
|
| 392 |
parser.add_argument("--no-tunnel", action="store_true", help="без публичной ссылки")
|
| 393 |
parser.add_argument("--keep", type=int, default=0, help="держать N секунд и выйти")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
parser.add_argument("--skip-audio", action="store_true",
|
| 395 |
help="не ставить fluidsynth (рендер аудио будет недоступен)")
|
|
|
|
|
|
|
|
|
|
| 396 |
parser.add_argument("--instruments", default=BAND_INSTRUMENTS,
|
| 397 |
help="инструменты для модели через запятую; auto = пусть решает сама")
|
| 398 |
args = parser.parse_args(argv)
|
|
@@ -407,10 +442,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 407 |
ensure_audio_tools()
|
| 408 |
ensure_ssh()
|
| 409 |
ensure_key()
|
| 410 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
say("[1/2] сервер отвечает: http://127.0.0.1:%d" % args.port)
|
| 412 |
proxy_port = args.port + 1
|
| 413 |
-
proxy = start_proxy(args.port, proxy_port, args.instruments)
|
| 414 |
if proxy.poll() is None:
|
| 415 |
say(" поток защищён прокси: http://127.0.0.1:%d" % proxy_port)
|
| 416 |
else:
|
|
@@ -445,10 +485,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 445 |
break
|
| 446 |
if server.poll() is not None:
|
| 447 |
say("сервер упал, поднимаю заново")
|
| 448 |
-
server = start_server(args.port, args.model, args.device, args.dtype)
|
| 449 |
if proxy.poll() is not None:
|
| 450 |
say("прокси упал, поднимаю заново")
|
| 451 |
-
proxy = start_proxy(args.port, proxy_port, args.instruments
|
|
|
|
| 452 |
if tunnel is not None and not tunnel.alive():
|
| 453 |
say("туннель умер, открываю новый")
|
| 454 |
tunnel.kill()
|
|
|
|
| 90 |
return False
|
| 91 |
|
| 92 |
|
| 93 |
+
def low_memory_env(threads: int | None = None) -> dict:
|
| 94 |
+
"""Убрать раздувание памяти аллокаторами, не трогая точность модели.
|
| 95 |
+
|
| 96 |
+
Веса остаются float32, никакого квантования: экономия идёт за счёт числа
|
| 97 |
+
потоков (у каждого своя арена malloc) и возврата освобождённых страниц ОС.
|
| 98 |
+
"""
|
| 99 |
+
cores = os.cpu_count() or 4
|
| 100 |
+
count = threads or max(1, min(4, cores // 2))
|
| 101 |
+
env = dict(os.environ)
|
| 102 |
+
env.update({
|
| 103 |
+
"OMP_NUM_THREADS": str(count),
|
| 104 |
+
"MKL_NUM_THREADS": str(count),
|
| 105 |
+
"OPENBLAS_NUM_THREADS": str(count),
|
| 106 |
+
"NUMEXPR_NUM_THREADS": str(count),
|
| 107 |
+
"MALLOC_ARENA_MAX": "2",
|
| 108 |
+
"MALLOC_TRIM_THRESHOLD_": "1000000",
|
| 109 |
+
"MALLOC_MMAP_THRESHOLD_": "131072",
|
| 110 |
+
"TOKENIZERS_PARALLELISM": "false",
|
| 111 |
+
"PYTORCH_CPU_ALLOC_CONF": "garbage_collection_threshold:0.6",
|
| 112 |
+
})
|
| 113 |
+
return env
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def start_server(port: int, model: str, device: str, dtype: str | None,
|
| 117 |
+
env: dict | None = None) -> subprocess.Popen:
|
| 118 |
command = [sys.executable, "-m", "muscriptor", "serve",
|
| 119 |
"--model", model, "--device", device, "--host", "0.0.0.0", "--port", str(port)]
|
| 120 |
if dtype:
|
|
|
|
| 122 |
log = open("muscriptor_server.log", "a")
|
| 123 |
say("запускаю: " + " ".join(command[1:]))
|
| 124 |
process = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT,
|
| 125 |
+
stdin=open(os.devnull, "rb"),
|
| 126 |
+
env=env or dict(os.environ))
|
| 127 |
deadline = time.time() + 600
|
| 128 |
started = time.time()
|
| 129 |
while time.time() < deadline:
|
|
|
|
| 367 |
|
| 368 |
|
| 369 |
def start_proxy(server_port: int, listen_port: int,
|
| 370 |
+
instruments: str = BAND_INSTRUMENTS,
|
| 371 |
+
multi_pass: str = "") -> subprocess.Popen:
|
| 372 |
"""SSE-прокси: держит поток /transcribe живым через медленный туннель."""
|
| 373 |
script = pathlib.Path(__file__).with_name("sse_proxy.py")
|
| 374 |
if not script.exists():
|
|
|
|
| 376 |
proc = subprocess.Popen(
|
| 377 |
[sys.executable, "-u", str(script),
|
| 378 |
"--listen", str(listen_port), "--target", str(server_port),
|
| 379 |
+
"--instruments", instruments]
|
| 380 |
+
+ (["--multi-pass", multi_pass] if multi_pass else []),
|
| 381 |
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
| 382 |
for _ in range(20):
|
| 383 |
time.sleep(0.5)
|
|
|
|
| 418 |
help="имя пользователя для локального туннеля (по умолчанию nokey)")
|
| 419 |
parser.add_argument("--no-tunnel", action="store_true", help="без публичной ссылки")
|
| 420 |
parser.add_argument("--keep", type=int, default=0, help="держать N секунд и выйти")
|
| 421 |
+
parser.add_argument("--low-memory", action="store_true",
|
| 422 |
+
help="экономный режим для слабого ПК: меньше потоков и арен,"
|
| 423 |
+
" веса всё так же float32")
|
| 424 |
+
parser.add_argument("--threads", type=int, default=None,
|
| 425 |
+
help="сколько потоков дать модели (по умолчанию половина ядер)")
|
| 426 |
parser.add_argument("--skip-audio", action="store_true",
|
| 427 |
help="не ставить fluidsynth (рендер аудио будет недоступен)")
|
| 428 |
+
parser.add_argument("--multi-pass", default="",
|
| 429 |
+
help="второй проход с другим набором инструментов и слияние,"
|
| 430 |
+
" например: chromatic_percussion,electric_bass,acoustic_bass")
|
| 431 |
parser.add_argument("--instruments", default=BAND_INSTRUMENTS,
|
| 432 |
help="инструменты для модели через запятую; auto = пусть решает сама")
|
| 433 |
args = parser.parse_args(argv)
|
|
|
|
| 442 |
ensure_audio_tools()
|
| 443 |
ensure_ssh()
|
| 444 |
ensure_key()
|
| 445 |
+
env = None
|
| 446 |
+
if args.low_memory or args.threads:
|
| 447 |
+
env = low_memory_env(args.threads)
|
| 448 |
+
say(" экономный режим: потоков %s, арен malloc 2, веса float32"
|
| 449 |
+
% env["OMP_NUM_THREADS"])
|
| 450 |
+
server = start_server(args.port, args.model, args.device, args.dtype, env)
|
| 451 |
say("[1/2] сервер отвечает: http://127.0.0.1:%d" % args.port)
|
| 452 |
proxy_port = args.port + 1
|
| 453 |
+
proxy = start_proxy(args.port, proxy_port, args.instruments, args.multi_pass)
|
| 454 |
if proxy.poll() is None:
|
| 455 |
say(" поток защищён прокси: http://127.0.0.1:%d" % proxy_port)
|
| 456 |
else:
|
|
|
|
| 485 |
break
|
| 486 |
if server.poll() is not None:
|
| 487 |
say("сервер упал, поднимаю заново")
|
| 488 |
+
server = start_server(args.port, args.model, args.device, args.dtype, env)
|
| 489 |
if proxy.poll() is not None:
|
| 490 |
say("прокси упал, поднимаю заново")
|
| 491 |
+
proxy = start_proxy(args.port, proxy_port, args.instruments,
|
| 492 |
+
args.multi_pass)
|
| 493 |
if tunnel is not None and not tunnel.alive():
|
| 494 |
say("туннель умер, открываю новый")
|
| 495 |
tunnel.kill()
|