2026-05-25 16:59:59 +08:00
|
|
|
|
"""华胥卡车主动安全(AEB)平台根配置。"""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from urllib.parse import quote_plus
|
|
|
|
|
|
|
|
|
|
|
|
AS_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
|
|
|
|
WORKSPACE = AS_ROOT
|
|
|
|
|
|
|
2026-06-03 11:40:21 +08:00
|
|
|
|
|
|
|
|
|
|
def resolve_workspace_root() -> Path | None:
|
|
|
|
|
|
"""外部 workspace(DMS/Lane 大文件);Docker 内通常为 /data/workspace。"""
|
|
|
|
|
|
candidates: list[Path] = []
|
|
|
|
|
|
explicit = os.environ.get("AS_WORKSPACE_ROOT", "").strip()
|
|
|
|
|
|
if explicit:
|
|
|
|
|
|
candidates.append(Path(explicit))
|
|
|
|
|
|
candidates.extend((Path("/data/workspace"), AS_ROOT.parent / "workspace"))
|
|
|
|
|
|
for cand in candidates:
|
|
|
|
|
|
try:
|
|
|
|
|
|
resolved = cand.resolve()
|
|
|
|
|
|
except OSError:
|
|
|
|
|
|
continue
|
|
|
|
|
|
if resolved.is_dir() and (resolved / "DMS").is_dir():
|
|
|
|
|
|
return resolved
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WORKSPACE_ROOT = resolve_workspace_root()
|
|
|
|
|
|
|
2026-05-25 16:59:59 +08:00
|
|
|
|
PLATFORM_DIR = AS_ROOT / "platform"
|
2026-06-03 11:40:21 +08:00
|
|
|
|
# Label Studio 工程构建产物(scripts/build_hsap_ls_ui.sh)
|
|
|
|
|
|
PLATFORM_WEB = PLATFORM_DIR / "ui-hsap" / "dist"
|
2026-05-25 16:59:59 +08:00
|
|
|
|
ALGORITHMS_DIR = AS_ROOT / "algorithms"
|
|
|
|
|
|
DATASETS_DIR = AS_ROOT / "datasets"
|
|
|
|
|
|
ALGORITHMS_REGISTRY = ALGORITHMS_DIR / "registry.yaml"
|
|
|
|
|
|
|
|
|
|
|
|
MANIFESTS = AS_ROOT / "manifests"
|
|
|
|
|
|
JOB_LOG = MANIFESTS / "job_log.jsonl"
|
|
|
|
|
|
APPROVAL_QUEUE = MANIFESTS / "approval_queue.jsonl"
|
|
|
|
|
|
TRACE_LOG = MANIFESTS / "trace_log.jsonl"
|
|
|
|
|
|
GRAPH_STATE_DIR = MANIFESTS / "graph_state"
|
|
|
|
|
|
ENGINES_REGISTRY = ALGORITHMS_REGISTRY
|
|
|
|
|
|
SQLITE_LEGACY_PATH = MANIFESTS / "platform.db"
|
|
|
|
|
|
|
|
|
|
|
|
# ── 数据库(默认 PostgreSQL)──
|
|
|
|
|
|
DB_HOST = os.environ.get("AS_DB_HOST", "127.0.0.1")
|
|
|
|
|
|
DB_PORT = os.environ.get("AS_DB_PORT", "5432")
|
|
|
|
|
|
DB_USER = os.environ.get("AS_DB_USER", "as_platform")
|
|
|
|
|
|
DB_PASSWORD = os.environ.get("AS_DB_PASSWORD", "as_platform")
|
|
|
|
|
|
DB_NAME = os.environ.get("AS_DB_NAME", "as_platform")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_database_url() -> str:
|
|
|
|
|
|
explicit = os.environ.get("AS_DATABASE_URL", "").strip()
|
|
|
|
|
|
if explicit:
|
|
|
|
|
|
return explicit
|
|
|
|
|
|
pwd = quote_plus(DB_PASSWORD)
|
|
|
|
|
|
return f"postgresql+psycopg2://{DB_USER}:{pwd}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DATABASE_URL = build_database_url()
|
|
|
|
|
|
IS_POSTGRES = DATABASE_URL.startswith("postgresql")
|
|
|
|
|
|
IS_SQLITE = DATABASE_URL.startswith("sqlite")
|
|
|
|
|
|
|
|
|
|
|
|
# ── Redis(Job 队列 / 事件,docker compose 默认 redis:6379)──
|
|
|
|
|
|
REDIS_URL = os.environ.get("AS_REDIS_URL", "redis://127.0.0.1:6379/0")
|
|
|
|
|
|
JOB_QUEUE_KEY = os.environ.get("AS_JOB_QUEUE_KEY", "as:job_queue")
|
|
|
|
|
|
# thread: API 进程内执行(本机 run_local.sh)| worker: 推 Redis,由 worker 容器/脚本消费
|
|
|
|
|
|
JOB_EXECUTOR = os.environ.get("AS_JOB_EXECUTOR", "thread")
|
|
|
|
|
|
|
|
|
|
|
|
# ── 认证 / 飞书 ──
|
|
|
|
|
|
JWT_SECRET = os.environ.get("AS_JWT_SECRET", "change-me-in-production")
|
|
|
|
|
|
JWT_EXPIRE_HOURS = int(os.environ.get("AS_JWT_EXPIRE_HOURS", "168"))
|
|
|
|
|
|
FEISHU_APP_ID = os.environ.get("FEISHU_APP_ID", "")
|
|
|
|
|
|
FEISHU_APP_SECRET = os.environ.get("FEISHU_APP_SECRET", "")
|
|
|
|
|
|
FEISHU_REDIRECT_URI = os.environ.get(
|
|
|
|
|
|
"FEISHU_REDIRECT_URI",
|
|
|
|
|
|
"http://127.0.0.1:8787/api/v1/auth/feishu/callback",
|
|
|
|
|
|
)
|
|
|
|
|
|
FRONTEND_URL = os.environ.get("AS_FRONTEND_URL", "http://127.0.0.1:8787")
|
|
|
|
|
|
DEV_AUTH_ENABLED = os.environ.get("AS_DEV_AUTH", "").lower() in ("1", "true", "yes")
|
2026-06-03 11:40:21 +08:00
|
|
|
|
FORCE_DEV_AUTH = os.environ.get("AS_FORCE_DEV_AUTH", "").lower() in ("1", "true", "yes")
|
2026-05-25 16:59:59 +08:00
|
|
|
|
FEISHU_ADMIN_OPEN_IDS = {
|
|
|
|
|
|
x.strip() for x in os.environ.get("FEISHU_ADMIN_OPEN_IDS", "").split(",") if x.strip()
|
|
|
|
|
|
}
|
|
|
|
|
|
FEISHU_ADMIN_DEPARTMENT_IDS = {
|
|
|
|
|
|
x.strip() for x in os.environ.get("FEISHU_ADMIN_DEPARTMENT_IDS", "").split(",") if x.strip()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 11:40:21 +08:00
|
|
|
|
# ── 飞书多维表格(内网出站同步)──
|
|
|
|
|
|
FEISHU_BITABLE_APP_TOKEN = os.environ.get("FEISHU_BITABLE_APP_TOKEN", "").strip()
|
|
|
|
|
|
# 表在知识库/wiki 内时:填 wiki 链接里 /wiki/ 后节点 ID;HSAP 会调 API 解析为 Basc obj_token
|
|
|
|
|
|
FEISHU_BITABLE_WIKI_NODE_TOKEN = os.environ.get("FEISHU_BITABLE_WIKI_NODE_TOKEN", "").strip()
|
|
|
|
|
|
FEISHU_BITABLE_TABLE_ID = os.environ.get("FEISHU_BITABLE_TABLE_ID", "").strip()
|
|
|
|
|
|
FEISHU_LABELING_CHAT_ID = os.environ.get("FEISHU_LABELING_CHAT_ID", "").strip()
|
|
|
|
|
|
FEISHU_BITABLE_SYNC_ENABLED = os.environ.get("FEISHU_BITABLE_SYNC_ENABLED", "").lower() in (
|
|
|
|
|
|
"1",
|
|
|
|
|
|
"true",
|
|
|
|
|
|
"yes",
|
|
|
|
|
|
)
|
|
|
|
|
|
FEISHU_BITABLE_SYNC_INTERVAL_SEC = max(30, int(os.environ.get("FEISHU_BITABLE_SYNC_INTERVAL_SEC", "120")))
|
|
|
|
|
|
FEISHU_BITABLE_AUTO_INGEST = os.environ.get("FEISHU_BITABLE_AUTO_INGEST", "").lower() in (
|
|
|
|
|
|
"1",
|
|
|
|
|
|
"true",
|
|
|
|
|
|
"yes",
|
|
|
|
|
|
)
|
|
|
|
|
|
FEISHU_BITABLE_WEBHOOK_ENABLED = os.environ.get("FEISHU_BITABLE_WEBHOOK_ENABLED", "").lower() in (
|
|
|
|
|
|
"1",
|
|
|
|
|
|
"true",
|
|
|
|
|
|
"yes",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 中文列名(与 FEISHU_BITABLE_OPS.md 一致,可用环境变量覆盖)
|
|
|
|
|
|
def _field(name: str, default: str) -> str:
|
|
|
|
|
|
return os.environ.get(name, default).strip() or default
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FEISHU_BITABLE_FIELDS: dict[str, str] = {
|
|
|
|
|
|
"delivery_id": _field("FEISHU_BITABLE_FIELD_DELIVERY_ID", "批次编号"),
|
|
|
|
|
|
"project": _field("FEISHU_BITABLE_FIELD_PROJECT", "项目"),
|
|
|
|
|
|
"task": _field("FEISHU_BITABLE_FIELD_TASK", "任务"),
|
|
|
|
|
|
"mode": _field("FEISHU_BITABLE_FIELD_MODE", "子模式"),
|
|
|
|
|
|
"batch_name": _field("FEISHU_BITABLE_FIELD_BATCH_NAME", "批次名"),
|
|
|
|
|
|
"data_path": _field("FEISHU_BITABLE_FIELD_DATA_PATH", "数据路径"),
|
|
|
|
|
|
"status": _field("FEISHU_BITABLE_FIELD_STATUS", "状态"),
|
|
|
|
|
|
"candidate_id": _field("FEISHU_BITABLE_FIELD_CANDIDATE_ID", "候选ID"),
|
|
|
|
|
|
"campaign_id": _field("FEISHU_BITABLE_FIELD_CAMPAIGN_ID", "活动ID"),
|
|
|
|
|
|
"inbox_path": _field("FEISHU_BITABLE_FIELD_INBOX_PATH", "Inbox路径"),
|
|
|
|
|
|
"progress": _field("FEISHU_BITABLE_FIELD_PROGRESS", "HSAP进度"),
|
|
|
|
|
|
"hsap_link": _field("FEISHU_BITABLE_FIELD_HSAP_LINK", "HSAP链接"),
|
|
|
|
|
|
"error_message": _field("FEISHU_BITABLE_FIELD_ERROR_MESSAGE", "失败原因"),
|
|
|
|
|
|
"last_sync": _field("FEISHU_BITABLE_FIELD_LAST_SYNC", "最后同步"),
|
|
|
|
|
|
"record_id": _field("FEISHU_BITABLE_FIELD_RECORD_ID", "记录ID"),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# HSAP stage → 飞书表状态
|
|
|
|
|
|
FEISHU_STATUS_FROM_STAGE: dict[str, str] = {
|
|
|
|
|
|
"raw_pool": "待送标",
|
|
|
|
|
|
"out_for_labeling": "标注中",
|
|
|
|
|
|
"returned": "待入库",
|
|
|
|
|
|
"ingested": "已入库",
|
|
|
|
|
|
"labeling_submitted": "标注中",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-25 16:59:59 +08:00
|
|
|
|
# ── 功能开关 ──
|
|
|
|
|
|
# 车道线 catalog 质检统计(mask 抽样,较慢);暂时默认关闭
|
|
|
|
|
|
LANE_DATA_VIZ_ENABLED = os.environ.get("AS_LANE_DATA_VIZ", "").lower() in ("1", "true", "yes")
|
2026-06-03 11:40:21 +08:00
|
|
|
|
|
|
|
|
|
|
# ── 车队地图 / T-Box ──
|
|
|
|
|
|
FLEET_MAP_ENABLED = os.environ.get("AS_FLEET_MAP_ENABLED", "1").lower() in ("1", "true", "yes")
|
|
|
|
|
|
FLEET_MOCK_SEED = os.environ.get("AS_FLEET_MOCK_SEED", "1").lower() in ("1", "true", "yes")
|
|
|
|
|
|
FLEET_MOCK_SIMULATE = os.environ.get("AS_FLEET_MOCK_SIMULATE", "1").lower() in ("1", "true", "yes")
|
|
|
|
|
|
FLEET_SIM_INTERVAL_SEC = int(os.environ.get("AS_FLEET_SIM_INTERVAL_SEC", "8"))
|
|
|
|
|
|
AMAP_KEY = os.environ.get("AS_AMAP_KEY", "").strip()
|
|
|
|
|
|
# 车队地图瓦片:gaode(国内默认)| osm
|
|
|
|
|
|
MAP_TILE_PROVIDER = os.environ.get("AS_MAP_TILE_PROVIDER", "gaode").strip().lower() or "gaode"
|
|
|
|
|
|
TBOX_INGEST_TOKEN = os.environ.get("AS_TBOX_INGEST_TOKEN", "hsap-demo-tbox-token").strip()
|