feat: parse EXTRA_BODY as a JSON object per source

This commit is contained in:
2026-07-31 21:12:06 -04:00
parent 6023d11bfb
commit 152fa264ed
2 changed files with 37 additions and 0 deletions
+8
View File
@@ -11,6 +11,7 @@ fail-loud 校验语义与 pydantic-settings 一致。
from __future__ import annotations from __future__ import annotations
import json
import os import os
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -44,6 +45,7 @@ _SOURCE_FIELDS: dict[str, tuple[str, str]] = {
"ENABLE_THINKING": ("enable_thinking", "bool"), "ENABLE_THINKING": ("enable_thinking", "bool"),
"MISSING_DONE": ("missing_done", "str"), "MISSING_DONE": ("missing_done", "str"),
"TRUST_ENV": ("trust_env", "bool"), "TRUST_ENV": ("trust_env", "bool"),
"EXTRA_BODY": ("extra_body", "json"),
} }
_RESERVED_SEGMENTS = frozenset({"GLOBAL", "RETRY", "BREAKER", "BACKPRESSURE"}) _RESERVED_SEGMENTS = frozenset({"GLOBAL", "RETRY", "BREAKER", "BACKPRESSURE"})
_SELECTORS = frozenset({"round_robin", "least_inflight", "health_aware"}) _SELECTORS = frozenset({"round_robin", "least_inflight", "health_aware"})
@@ -74,6 +76,12 @@ def _cast(raw: str, kind: str, key: str) -> object:
if lowered in ("0", "false", "no", "off"): if lowered in ("0", "false", "no", "off"):
return False return False
raise ValueError(f"非法布尔值: {raw!r}") raise ValueError(f"非法布尔值: {raw!r}")
if kind == "json":
# JSONDecodeError 是 ValueError 子类,复用下方的统一包装
parsed = json.loads(raw)
if not isinstance(parsed, dict):
raise ValueError(f"必须是 JSON 对象(而非数组/标量): {raw!r}")
return parsed
return raw return raw
except ValueError as exc: except ValueError as exc:
raise ValueError(f"配置 {key} 解析失败: {exc}") from exc raise ValueError(f"配置 {key} 解析失败: {exc}") from exc
+29
View File
@@ -103,6 +103,35 @@ class TestSourceAggregation:
GatewaySettings.from_env("LLM", env=env) GatewaySettings.from_env("LLM", env=env)
class TestExtraBodyParsing:
"""配置级采样参数的 env 解析(issue #4 Task 2)。"""
def test_json_object_parsed(self):
env = _env(**{"LLM__QWEN__1__EXTRA_BODY": '{"temperature": 0, "seed": 42}'})
s = GatewaySettings.from_env("LLM", env=env)
assert s.sources[0].extra_body == {"temperature": 0, "seed": 42}
def test_absent_defaults_to_empty(self):
assert GatewaySettings.from_env("LLM", env=_env()).sources[0].extra_body == {}
def test_invalid_json_fails_loudly(self):
env = _env(**{"LLM__QWEN__1__EXTRA_BODY": "{invalid"})
with pytest.raises(ValueError, match="EXTRA_BODY"):
GatewaySettings.from_env("LLM", env=env)
def test_non_object_json_fails(self):
"""数组/标量都不是请求体片段,静默接受会让参数悄悄不生效。"""
env = _env(**{"LLM__QWEN__1__EXTRA_BODY": "[1, 2]"})
with pytest.raises(ValueError, match="JSON 对象"):
GatewaySettings.from_env("LLM", env=env)
def test_protected_key_rejected_through_assembly(self):
"""校验确实挂在装配路径上(而非只在 types.py 里孤立存在)。"""
env = _env(**{"LLM__QWEN__1__EXTRA_BODY": '{"model": "sneaky"}'})
with pytest.raises(ValueError, match="model"):
GatewaySettings.from_env("LLM", env=env)
class TestResilienceKeys: class TestResilienceKeys:
def test_flat_legacy_keys(self): def test_flat_legacy_keys(self):
s = GatewaySettings.from_env("LLM", env=_env()) s = GatewaySettings.from_env("LLM", env=_env())