Update consumable pipeline, client API docs, and deployment config

- Refine effective candidate consumables and classifier labels
- Adjust vision algorithm, TSV logging, and video session wiring
- Refresh client surgery HTTP contract doc and staging/video docs
- Update settings, docker-compose prod, tests, and uv.lock

Made-with: Cursor
This commit is contained in:
Kevin
2026-04-24 11:05:17 +08:00
parent 3d7bd70355
commit 557fcee803
15 changed files with 529 additions and 636 deletions

View File

@@ -58,7 +58,7 @@ class _VideoGroup(_SettingsGroup):
"consumable_classifier_device",
"consumable_classifier_topk",
"consumable_min_cls_confidence",
"consumable_catalog_xlsx_path",
"consumable_classifier_labels_yaml_path",
"consumable_vision_window_sec",
"hand_detection_weights",
"hand_detection_imgsz",
@@ -172,6 +172,11 @@ def _default_camera_rtsp_urls_sample_path() -> str:
return str(_PACKAGE_DIR / "resources" / "camera_rtsp_urls.sample.json")
def _default_consumable_classifier_labels_yaml() -> str:
"""与分类训练类名、业务 `label_id` 对照的 YAML见 `app/resources/consumable_classifier_labels.yaml`。"""
return str(_PACKAGE_DIR / "resources" / "consumable_classifier_labels.yaml")
class Settings(BaseSettings):
"""Application configuration loaded from environment / .env."""
@@ -199,8 +204,8 @@ class Settings(BaseSettings):
consumable_classifier_topk: int = 5
#: 耗材分类 top1 最低置信度(手部 ROI 或全帧送入分类器后的门槛)。
consumable_min_cls_confidence: float = Field(default=0.5, ge=0.0, le=1.0)
#: 可选:`视频中的商品信息表.xlsx`(含「商品名称」「产品编码」);空则物品 id 用名称本身
consumable_catalog_xlsx_path: str = ""
#: 分类类名 + 业务 `label_id` 对照(与训练 `names` 一致);`build_name_mapping` 将识别出的类名匹配至此得到业务 id。空则使用下方默认包内文件
consumable_classifier_labels_yaml_path: str = ""
#: 与离线脚本一致的时间窗(秒);窗内多次推理取众数后再走自动记账 / 语音追问逻辑。
consumable_vision_window_sec: float = Field(default=15.0, ge=0.5, le=600.0)
#: 手部检测 YOLO 权重;空或文件不存在时退化为「全帧送分类器」(兼容仅有关分类权重的环境)。
@@ -354,6 +359,13 @@ class Settings(BaseSettings):
return _default_consumable_classifier_weights()
return str(value)
@field_validator("consumable_classifier_labels_yaml_path", mode="before")
@classmethod
def consumable_classifier_labels_yaml_path_default(cls, value: object) -> str:
if value is None or str(value).strip() == "":
return _default_consumable_classifier_labels_yaml()
return str(value).strip()
model_config = SettingsConfigDict(
env_file=(str(_DEFAULT_ENV_FILE),),
env_file_encoding="utf-8",