Files
talkscore-asr/app/launcher.py
T
Vladimir BryzgalovandClaude Opus 5 b1b09664b9 Сервис сам докачивает недостающие мелкие модели
Обновление возит только папку app, поэтому переход на детектор речи
потребовал файла, которого на машине нет - и сервис узнал об этом на
первой же задаче: отказ выглядел сбоем обработки, хотя это незаконченная
установка. Причём download_models.py лежит вне app и тоже был старым,
так что штатный способ докачки не помог бы.

Список моделей и адреса переехали в app/models.py. Мелкие докачиваются
при запуске, крупная модель распознавания - нет: сотни мегабайт без спроса
тянуть нельзя. Нехватка видна в /health полем missing_models.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 15:42:54 +05:00

143 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Запуск сервиса: обновление, Caddyfile, HTTPS.
Раньше вся эта последовательность жила в .bat-файлах, а Caddyfile правился
руками. Оба файла лежат вне папки app, поэтому обновление их не доставляло:
чтобы получить новый порядок запуска, приходилось переустанавливать сервис.
Теперь .bat - это трёхстрочная заглушка, которая просто зовёт этот модуль,
а Caddyfile создаётся здесь из настроек. Меняется только код внутри app,
который обновление возит штатно.
"""
import subprocess
import sys
from pathlib import Path
__all__ = ["render_caddyfile", "ensure_caddyfile", "main"]
# По этой строке отличаем свой файл от отредактированного вручную.
MARK = "# Создан автоматически talkscore-asr. Правки будут перезаписаны."
CADDY_WAIT_SEC = 3
def render_caddyfile(domains: str, port: int, max_upload_mb: int) -> str:
"""Собирает конфигурацию Caddy из настроек сервиса."""
listed = ", ".join(d.strip() for d in domains.split(",") if d.strip())
# Пустой строки перед блоком быть не должно: caddy fmt считает это
# неформатированным файлом и ругается при каждом запуске.
return f"""{MARK}
# Домены и предел размера берутся из config.toml, менять нужно там.
{{
\t# Caddy очень подробно рассказывает про сертификаты. В консоли из-за этого
\t# не видно сообщений сервиса, поэтому его вывод уходит в файл.
\tlog {{
\t\toutput file logs/caddy.log {{
\t\t\troll_size 10MiB
\t\t\troll_keep 5
\t\t}}
\t\tformat console
\t\tlevel INFO
\t}}
}}
{listed} {{
\t# X-Forwarded-For здесь намеренно не задаётся: Caddy сам игнорирует этот
\t# заголовок из входящего запроса и подставляет реальный адрес клиента.
\t# Не добавляйте сюда trusted_proxies - тогда Caddy начнёт верить заголовку
\t# от клиента, и любой сможет назваться разрешённым адресом.
\treverse_proxy 127.0.0.1:{port}
\trequest_body {{
\t\tmax_size {max_upload_mb}MB
\t}}
}}
"""
def ensure_caddyfile(base_dir: Path, content: str) -> Path | None:
"""Записывает Caddyfile, не затирая ручные правки.
Если файл создан не нами, откладываем новую версию рядом: молча стереть
чужую настройку хуже, чем оставить её устаревшей.
"""
target = base_dir / "Caddyfile"
if target.exists():
existing = target.read_text(encoding="utf-8", errors="replace")
if MARK not in existing:
spare = base_dir / "Caddyfile.new"
if existing.strip() != content.strip():
spare.write_text(content, encoding="utf-8")
return spare
return None
if existing == content:
return None
target.write_text(content, encoding="utf-8")
return target
def start_caddy(base_dir: Path) -> subprocess.Popen | None:
"""Поднимает Caddy отдельным процессом. Его отсутствие не должно ронять запуск."""
binary = base_dir / "bin" / "caddy.exe"
if not binary.is_file():
binary = base_dir / "bin" / "caddy"
if not binary.is_file():
print("\n Caddy не найден в папке bin - запускаюсь без HTTPS.\n")
return None
(base_dir / "logs").mkdir(exist_ok=True)
return subprocess.Popen(
[str(binary), "run", "--config", str(base_dir / "Caddyfile")],
cwd=str(base_dir))
def main(argv: list[str] | None = None) -> int:
argv = sys.argv[1:] if argv is None else argv
https = "https" in argv
from app import updater
from app.config import ConfigError, load_settings
try:
settings = load_settings()
except ConfigError as exc:
print(f"\n {exc}\n")
return 1
updater.main()
# Модели проверяются и докачиваются до запуска: узнавать о нехватке
# на первой же задаче неправильно - это незаконченная установка,
# а выглядит как сбой обработки.
from app.models import ensure_models
for path in ensure_models(settings.models_dir, settings.diarize):
print(f"\n Не хватает модели: {path.name}"
"\n Запустите download_models.bat - сервис поднимется,"
"\n но принимать записи не сможет.\n")
caddy = None
if https:
if not settings.https_domains.strip():
print("\n В config.toml не заданы домены: раздел [https], ключ domains."
"\n Без них сертификат выпустить не на что.\n")
return 1
written = ensure_caddyfile(
settings.base_dir,
render_caddyfile(settings.https_domains, settings.port,
settings.max_upload_mb))
if written is not None and written.name.endswith(".new"):
print(f"\n Caddyfile правили вручную, поэтому не трогаю его."
f"\n Новая версия отложена рядом: {written}\n")
caddy = start_caddy(settings.base_dir)
try:
from app.main import run
run()
finally:
if caddy is not None:
caddy.terminate()
return 0
if __name__ == "__main__":
sys.exit(main())