feat: add OcrSettings scope configuration
This commit is contained in:
@@ -407,3 +407,29 @@ class EmbeddingSettings:
|
||||
normalize=bool(_cast(norm[1], "bool", norm[0])) if norm else False,
|
||||
expected_dim=expected_dim,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class OcrSettings:
|
||||
"""OCR scope 装配配置(M3 设计 §3.4): 复用 GatewaySettings,无 OCR 专用键。
|
||||
|
||||
cache/structured/pricing 键对 OCR 无意义,装配时忽略;TPM 闸不启用
|
||||
(tpm=0)、看门狗键不配(非流式)由源键缺省自然达成。api_key 惯例填
|
||||
"none"(MonkeyOCR 无鉴权,SourceConfig 非空校验用占位)。
|
||||
"""
|
||||
|
||||
gateway: GatewaySettings
|
||||
|
||||
@classmethod
|
||||
def from_env(
|
||||
cls,
|
||||
scope: str = "OCR",
|
||||
env: Mapping[str, str] | None = None,
|
||||
*,
|
||||
env_file: str = ".env",
|
||||
) -> OcrSettings:
|
||||
if env is None:
|
||||
env = {
|
||||
k: v for k, v in {**dotenv_values(env_file), **os.environ}.items() if v is not None
|
||||
}
|
||||
return cls(gateway=GatewaySettings.from_env(scope.upper(), env=env))
|
||||
|
||||
@@ -274,3 +274,48 @@ class TestAssemblyGuards:
|
||||
env_ok = dict(env)
|
||||
env_ok["LLM__BACKPRESSURE__STALL_WINDOW_S"] = "60"
|
||||
assert GatewaySettings.from_env("LLM", env=env_ok).backpressure.stall_window_s == 60.0
|
||||
|
||||
|
||||
class TestOcrSettings:
|
||||
"""M3 OcrSettings(设计 §3.4): 复用 GatewaySettings,无 OCR 专用键。"""
|
||||
|
||||
_OCR_ENV = {
|
||||
"OCR__MONKEY__1__BASE_URL": "http://10.77.0.20:7866",
|
||||
"OCR__MONKEY__1__API_KEY": "none", # 无鉴权占位惯例
|
||||
"OCR__MONKEY__1__MODEL": "monkey-ocr",
|
||||
"OCR__MONKEY__1__TIMEOUT_S": "120",
|
||||
"LLM_MAX_RETRIES": "3",
|
||||
"LLM_RETRY_BASE_DELAY": "2.0",
|
||||
"LLM_RETRY_MAX_DELAY": "30.0",
|
||||
"LLM_CIRCUIT_BREAKER_THRESHOLD": "5",
|
||||
"LLM_CIRCUIT_BREAKER_COOLDOWN": "60",
|
||||
"PGW_CACHE_BACKEND": "none",
|
||||
"PGW_TELEMETRY_BACKEND": "none",
|
||||
}
|
||||
|
||||
def test_minimal_ocr_scope(self):
|
||||
from polygateway.config import OcrSettings
|
||||
|
||||
settings = OcrSettings.from_env("OCR", env=dict(self._OCR_ENV))
|
||||
gw = settings.gateway
|
||||
assert gw.scope == "ocr" # GatewaySettings 统一小写归一(既有约定)
|
||||
assert gw.sources[0].name == "monkey_1"
|
||||
assert gw.sources[0].provider == "monkey"
|
||||
assert gw.sources[0].trust_env is True
|
||||
|
||||
def test_scope_resilience_override(self):
|
||||
from polygateway.config import OcrSettings
|
||||
|
||||
env = dict(self._OCR_ENV)
|
||||
env["OCR__RETRY__MAX_ATTEMPTS"] = "5"
|
||||
env["OCR__MONKEY__1__TRUST_ENV"] = "false"
|
||||
settings = OcrSettings.from_env("OCR", env=env)
|
||||
assert settings.gateway.retry.max_attempts == 5
|
||||
assert settings.gateway.sources[0].trust_env is False
|
||||
|
||||
def test_missing_base_url_fails(self):
|
||||
from polygateway.config import OcrSettings
|
||||
|
||||
env = {k: v for k, v in self._OCR_ENV.items() if k != "OCR__MONKEY__1__BASE_URL"}
|
||||
with pytest.raises(ValueError):
|
||||
OcrSettings.from_env("OCR", env=env)
|
||||
|
||||
Reference in New Issue
Block a user