Files
talkscore-asr/app/config.py
T
Vladimir BryzgalovandClaude Opus 5 9dc67bda5c talkscore-asr 0.1.0: сервис транскрибации и диаризации
Локальный FastAPI-сервис поверх GigaAM v3 и sherpa-onnx: приём аудио,
очередь задач, разделение по говорящим, постобработка терминов.

Доставка на Windows - ZIP со встроенным Python, без установки чего-либо.
Обновление кода при запуске тянется из релизов Gitea: меняется только
папка app, десятки килобайт вместо всего пакета.

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

123 lines
5.0 KiB
Python

"""Настройки сервиса: читаются из config.toml рядом с исполняемым файлом."""
import os
import secrets
import tomllib
from dataclasses import dataclass, field
from pathlib import Path
__all__ = ["Settings", "load_settings", "BASE_DIR"]
# Корень пакета: при запуске из ZIP это папка, куда его распаковали.
BASE_DIR = Path(os.environ.get("TALKSCORE_ASR_HOME") or Path(__file__).resolve().parent.parent)
_DEFAULT_CONFIG = """# Настройки talkscore-asr
[server]
# 0.0.0.0 - принимать со всех интерфейсов, 127.0.0.1 - только с этой машины
host = "0.0.0.0"
port = 8756
[security]
# Токен для заголовка: Authorization: Bearer <token>
# Пустое значение = сервис никого не пустит. Сгенерирован при первом запуске.
token = "{token}"
# Кто может обращаться. Пусто = разрешены все адреса.
# Примеры: "192.168.1.0/24, 10.8.0.5, 127.0.0.1"
allow_ips = "127.0.0.1, ::1"
# Открывать ли /docs и /openapi.json. Они не требуют токена и показывают
# устройство API всем, кто знает адрес, поэтому по умолчанию выключены.
docs = false
[processing]
# Сколько потоков отдать распознаванию. 0 = половина ядер.
threads = 0
# Ожидаемое число говорящих в записи. 0 = определять автоматически
# (на реальных звонках работает плохо, для диалога ставьте 2).
speakers = 2
# Максимальный размер загружаемого файла, МБ
max_upload_mb = 500
# Сколько часов хранить результаты завершённых задач
keep_results_hours = 72
[update]
# Проверять обновления кода при каждом запуске. Обновляется только папка app,
# это десятки килобайт: Python, библиотеки и модели остаются на месте.
enabled = true
server = "https://git.netranking.ru"
repo = "bryzgalov/talkscore-asr"
# Токен Gitea с правом чтения репозитория. Для публичного репозитория не нужен.
token = ""
"""
@dataclass
class Settings:
host: str = "0.0.0.0"
port: int = 8756
token: str = ""
allow_ips: str = ""
docs: bool = False
threads: int = 0
speakers: int = 2
max_upload_mb: int = 500
keep_results_hours: float = 72.0
update_enabled: bool = False
update_server: str = "https://git.netranking.ru"
update_repo: str = "bryzgalov/talkscore-asr"
update_token: str = ""
base_dir: Path = field(default_factory=lambda: BASE_DIR)
@property
def models_dir(self) -> Path:
return self.base_dir / "models"
@property
def data_dir(self) -> Path:
return self.base_dir / "data"
@property
def replacements_path(self) -> Path:
return self.base_dir / "replacements.txt"
@property
def max_upload_bytes(self) -> int:
return self.max_upload_mb * 1024 * 1024
def effective_threads(self) -> int:
if self.threads > 0:
return self.threads
# Половина логических ядер: оставляем запас, чтобы машина не вставала колом
# во время обработки. На Ryzen 9 9950X это 16 потоков.
return max(1, (os.cpu_count() or 4) // 2)
def load_settings(config_path: Path | None = None) -> Settings:
"""Читает config.toml, создавая его с новым токеном при первом запуске."""
path = Path(config_path or BASE_DIR / "config.toml")
if not path.is_file():
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(_DEFAULT_CONFIG.format(token=secrets.token_urlsafe(32)), encoding="utf-8")
data = tomllib.loads(path.read_text(encoding="utf-8"))
server = data.get("server", {})
security = data.get("security", {})
proc = data.get("processing", {})
upd = data.get("update", {})
return Settings(
host=server.get("host", "0.0.0.0"),
port=int(server.get("port", 8756)),
token=str(security.get("token", "")),
allow_ips=str(security.get("allow_ips", "")),
docs=bool(security.get("docs", False)),
threads=int(proc.get("threads", 0)),
speakers=int(proc.get("speakers", 2)),
max_upload_mb=int(proc.get("max_upload_mb", 500)),
keep_results_hours=float(proc.get("keep_results_hours", 72)),
update_enabled=bool(upd.get("enabled", False)),
update_server=str(upd.get("server", "https://git.netranking.ru")),
update_repo=str(upd.get("repo", "bryzgalov/talkscore-asr")),
update_token=str(upd.get("token", "")),
base_dir=path.parent,
)