Files
audio2text/app/config.py
audio2text dev 73110848f4 feat: 调度器+并发管线+GPU优化+日志分级+前端修复
- scheduler: ffmpeg 异步线程 + GPU 串行调度 + 模型复用(2N→2 次加载)
- pipeline: 阶段拆分(extract/asr/translate),中间数据存 Task 字段
- translate_service: 长度排序批处理,padding 浪费减少 91%
- model_manager: ASR/翻译不共驻,BatchedInferencePipeline 批量解码
- 日志分级: INFO=任务流转里程碑,DEBUG=进度详情;默认 INFO
- 前端: 日志最新在上+滚动感知+退避轮询;24h 时间;上传中状态显示
- /health: 返回完整 Whisper/NLLB 配置
- upload_service: 单事务 complete + 扩展名白名单
- task_router: 合并 UploadSession 虚拟任务到列表
- Dockerfile: CPU/GPU 独立构建链,deps 缓存稳定
- prefetch_models: 安装时预下载模型权重
2026-07-06 21:59:59 +08:00

140 lines
4.6 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.

"""运行时配置:所有参数从 config.yaml 读取。
CPU dev / GPU prod 仅靠 device / model / compute_type 三项切换,代码完全不变。
默认值与 config.example.yaml 对齐,确保无 yaml 时也能用最小配置启动。
"""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
import yaml
from pydantic import BaseModel, Field, field_validator
PROJECT_ROOT = Path(__file__).resolve().parent.parent
DEFAULT_CONFIG_PATH = PROJECT_ROOT / "config.yaml"
class ServerConfig(BaseModel):
host: str = "0.0.0.0"
port: int = 8000
workers: int = 1
class StorageConfig(BaseModel):
upload_dir: str = "/data/uploads"
work_dir: str = "/data/.work"
output_dir: str = "/data/outputs"
chunk_bytes: int = 1024 * 1024
chunk_session_ttl_seconds: int = 300
# 缓存保留:超期任务产物(输出字幕 / 中间音频 / 保留的原始视频)连同 DB 记录一并清理
cache_retention_days: int = 7
# 定时清理间隔(小时):启动时跑一次,之后按此间隔循环
cache_cleanup_interval_hours: int = 24
class ProcessingConfig(BaseModel):
delete_original_after_extract: bool = True
keep_audio: bool = False
class AsrConfig(BaseModel):
model: str = "tiny.en" # CPU: tiny.enGPU: large-v3-turbo
device: str = "cpu" # cpu | cuda
compute_type: str = "int8" # cpu: int8gpu: float16
language: str = "en"
word_timestamps: bool = True
vad_filter: bool = True
batch_size: int = 8 # BatchedInferencePipeline 的音频块批大小GPU 建议 16
class TranslationConfig(BaseModel):
model: str = "Helsinki-NLP/opus-mt-en-zh" # CPU: opus-mt轻量GPU: facebook/nllb-200-distilled-1.3B
device: str = "cpu" # cpu | cuda
src_lang: str = "eng_Latn" # NLLB 语言码:英语
tgt_lang: str = "zho_Hans" # NLLB 语言码:简体中文
batch_size: int = 8 # 排序后单批最大条数CPU: 8GPU: 32显存独占可用大 batch
max_length: int = 256 # 单条最大生成 token 数
sort_by_length: bool = True # 按句子长度排序后分批,减少批内 padding 浪费GPU 收益大)
class SegmentationConfig(BaseModel):
max_words_per_line: int = 14
max_duration_seconds: float = 7.0
min_duration_seconds: float = 1.0
max_chars_per_line: int = 42
class LoggingConfig(BaseModel):
"""日志配置:控制台 + 内存缓冲的最低级别,以及缓冲条数。
分层语义:
- debug进度详情任务 [status pct%]、转写/翻译逐批统计、ffmpeg 命令)
- info任务流转里程碑音频提取/ASR/翻译 的开始与完成、模型加载与卸载)
- error详细错误完整 traceback由 logger.exception 自带)
"""
level: str = "info" # debug | info | warning | error
buffer_size: int = 2000 # /logs 页面内存缓冲条数
@field_validator("level", mode="before")
@classmethod
def _normalize_level(cls, v):
return str(v).lower() if v else "info"
class DocsConfig(BaseModel):
"""/docs Basic Auth 凭据(明文,对齐 server"""
enabled: bool = True
username: str = "admin"
password: str = ""
realm: str = "audio2text docs"
@field_validator("password", mode="before")
@classmethod
def _coerce(cls, v):
return "" if v is None else str(v)
class Settings(BaseModel):
server: ServerConfig = ServerConfig()
storage: StorageConfig = StorageConfig()
processing: ProcessingConfig = ProcessingConfig()
asr: AsrConfig = AsrConfig()
translation: TranslationConfig = TranslationConfig()
segmentation: SegmentationConfig = SegmentationConfig()
logging: LoggingConfig = LoggingConfig()
docs: DocsConfig = DocsConfig()
def upload_dir(self) -> Path:
return Path(self.storage.upload_dir)
def work_dir(self) -> Path:
return Path(self.storage.work_dir)
def output_dir(self) -> Path:
return Path(self.storage.output_dir)
def _load_yaml(path: Path) -> dict:
if not path.exists():
raise FileNotFoundError(
f"未找到配置文件 {path};请先 cp config.example.yaml config.yaml 并填值。"
)
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
@lru_cache(maxsize=1)
def get_settings() -> Settings:
path = Path(os.getenv("CONFIG_PATH", str(DEFAULT_CONFIG_PATH)))
return Settings.model_validate(_load_yaml(path))
def reload_settings() -> Settings:
"""清缓存并重新读取,供脚本与测试使用。"""
get_settings.cache_clear()
return get_settings()