feat: 设置页调 batch_size + 删除任务 + /docs 去认证 + 离线模式
设置页(/settings):
- 新增 Setting DB 模型(键值存储,持久化运行时覆盖)
- config.py 加 _apply_overrides:get_settings() 合并 DB 覆盖值
(_applying_overrides 标志防递归:DB 初始化回调 get_settings 时不重入)
- save_setting() 写 DB + 清 lru_cache,后续任务读到新值
- settings_router.py: GET/PUT /api/settings
- settings_html.py: 设置页表单(batch_size/beam_size/sort_by_length)
+ 设备只读信息 + 保存后 reload 确认
- 验证:改 asr_batch_size=16 beam_size=1 -> 任务 ASR 日志确认生效
- 验证:重启容器后设置从 DB 恢复(持久化)
删除任务:
- task_router.py: DELETE /api/tasks/{id},仅 done/failed 可删
删字幕/音频/视频产物 + UploadSession + Task DB 记录
- home_html.py + history_html.py: done/failed 任务显示删除按钮
+ confirm 确认 + 调用 DELETE API + 刷新列表
- 验证:删除 task 15 成功,删除进行中 task 返回 409
/docs 去认证:
- 移除 require_docs_auth 依赖,/docs /redoc /openapi.json 直接公开
离线模式:
- Dockerfile dev/final 加 HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1
(模型已缓存在 ./models volume,无需联网验证)
- 验证:离线模式完整跑通 ASR+翻译(task 15 done)
This commit is contained in:
102
app/config.py
102
app/config.py
@@ -128,13 +128,111 @@ def _load_yaml(path: Path) -> dict:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
|
||||
|
||||
# ---------------- 运行时覆盖 ----------------
|
||||
# 允许通过设置页修改的配置项(点分路径 -> 类型)。config.yaml 是只读挂载,
|
||||
# 改它需重启容器;运行时覆盖存 DB,进程重启后自动加载,无需重建镜像。
|
||||
# 设置页保存时调 save_setting() 写 DB + 清 lru_cache,下次 get_settings() 生效。
|
||||
_applying_overrides = False # 防递归标志:_apply_overrides 内部 DB 初始化会回调 get_settings()
|
||||
_OVERIDEABLE_FIELDS: dict[str, type] = {
|
||||
"asr.batch_size": int,
|
||||
"asr.beam_size": int,
|
||||
"translation.batch_size": int,
|
||||
"translation.sort_by_length": bool,
|
||||
}
|
||||
|
||||
|
||||
def _apply_overrides(settings: Settings) -> Settings:
|
||||
"""从 DB 读取覆盖值并应用到 Settings 对象。
|
||||
|
||||
在 lru_cache 的 get_settings() 内部调用,保证缓存的对象已含覆盖。
|
||||
DB 还没初始化时(首次 import)静默跳过,用 YAML 原值。
|
||||
|
||||
注意:get_session_local() -> get_engine() -> _db_path() -> get_settings()
|
||||
会形成递归。用 _applying_overrides 标志阻断:递归调用直接返回当前 settings
|
||||
(此时 DB 路径只需 work_dir,无覆盖也无妨)。
|
||||
"""
|
||||
global _applying_overrides
|
||||
if _applying_overrides:
|
||||
return settings # 递归调用(_db_path 触发),直接返回 YAML 原值
|
||||
_applying_overrides = True
|
||||
try:
|
||||
from .database import get_session_local
|
||||
from .models.setting import Setting
|
||||
import json
|
||||
db = get_session_local()()
|
||||
try:
|
||||
rows = db.query(Setting).all()
|
||||
overrides = {r.key: r.value for r in rows}
|
||||
finally:
|
||||
db.close()
|
||||
for key, type_ in _OVERIDEABLE_FIELDS.items():
|
||||
if key not in overrides:
|
||||
continue
|
||||
try:
|
||||
val = json.loads(overrides[key])
|
||||
val = type_(val)
|
||||
except (json.JSONDecodeError, ValueError, TypeError):
|
||||
continue
|
||||
_set_nested(settings, key, val)
|
||||
except Exception:
|
||||
# DB 未就绪(首次 import 时 database.py 可能还在初始化)-> 跳过,用 YAML 原值
|
||||
pass
|
||||
finally:
|
||||
_applying_overrides = False
|
||||
return settings
|
||||
|
||||
|
||||
def _set_nested(settings: Settings, key: str, val) -> None:
|
||||
"""按点分路径设置嵌套属性,如 'asr.batch_size' -> settings.asr.batch_size"""
|
||||
parts = key.split(".")
|
||||
obj = settings
|
||||
for p in parts[:-1]:
|
||||
obj = getattr(obj, p)
|
||||
setattr(obj, parts[-1], val)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_settings() -> Settings:
|
||||
"""读取 config.yaml + 应用 DB 覆盖,返回完整 Settings。
|
||||
|
||||
结果被 lru_cache 缓存。修改设置后调 reload_settings() 清缓存,
|
||||
下次调用返回含新值的 Settings。
|
||||
"""
|
||||
path = Path(os.getenv("CONFIG_PATH", str(DEFAULT_CONFIG_PATH)))
|
||||
return Settings.model_validate(_load_yaml(path))
|
||||
settings = Settings.model_validate(_load_yaml(path))
|
||||
return _apply_overrides(settings)
|
||||
|
||||
|
||||
def reload_settings() -> Settings:
|
||||
"""清缓存并重新读取,供脚本与测试使用。"""
|
||||
"""清缓存并重新读取(含 DB 覆盖),供设置页保存后调用。"""
|
||||
get_settings.cache_clear()
|
||||
return get_settings()
|
||||
|
||||
|
||||
def save_setting(key: str, value) -> None:
|
||||
"""保存单个配置项覆盖到 DB + 清 lru_cache。
|
||||
|
||||
Args:
|
||||
key: 点分路径,必须在 _OVERIDEABLE_FIELDS 中
|
||||
value: 要保存的值(自动 JSON 编码)
|
||||
"""
|
||||
import json
|
||||
if key not in _OVERIDEABLE_FIELDS:
|
||||
raise ValueError(f"不允许修改的配置项:{key}")
|
||||
from .database import get_session_local
|
||||
from .models.setting import Setting
|
||||
type_ = _OVERIDEABLE_FIELDS[key]
|
||||
encoded = json.dumps(type_(value))
|
||||
db = get_session_local()()
|
||||
try:
|
||||
row = db.get(Setting, key)
|
||||
if row is None:
|
||||
row = Setting(key=key, value=encoded)
|
||||
db.add(row)
|
||||
else:
|
||||
row.value = encoded
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
# 清缓存,让后续 get_settings() 读到新值
|
||||
get_settings.cache_clear()
|
||||
|
||||
Reference in New Issue
Block a user