fix(backend): 冻结 TOOLBOX 注册表为只读 MappingProxyType(CR-H7)

This commit is contained in:
Yaojia Wang
2026-07-08 10:41:15 +02:00
parent 5dd336b011
commit 25d48b7aa7
2 changed files with 18 additions and 1 deletions

View File

@@ -7,6 +7,7 @@
from __future__ import annotations from __future__ import annotations
import pytest
from ww_agents import SPECS from ww_agents import SPECS
from ww_skills import TOOLBOX, get_tool from ww_skills import TOOLBOX, get_tool
@@ -105,3 +106,11 @@ def test_get_tool_resolves_known_and_unknown() -> None:
assert get_tool("brainstorm") is TOOLBOX["brainstorm"] assert get_tool("brainstorm") is TOOLBOX["brainstorm"]
assert get_tool("worldbuilding") is TOOLBOX["worldbuilding"] assert get_tool("worldbuilding") is TOOLBOX["worldbuilding"]
assert get_tool("does-not-exist") is None assert get_tool("does-not-exist") is None
def test_toolbox_registry_is_read_only() -> None:
# CR-H7注册表是单一真相源运行时变异注入/删除)须 fail-fast冻结为 MappingProxyType
with pytest.raises(TypeError):
TOOLBOX["injected"] = TOOLBOX["brainstorm"] # type: ignore[index]
with pytest.raises(TypeError):
del TOOLBOX["brainstorm"] # type: ignore[attr-defined]

View File

@@ -16,6 +16,10 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
from types import MappingProxyType
from typing import Final
from ww_agents import SPECS from ww_agents import SPECS
from ww_agents.schemas import ( from ww_agents.schemas import (
BlurbResult, BlurbResult,
@@ -67,7 +71,7 @@ _SOURCE_TEXT_FIELD = InputField(
# 创作工具箱注册表key → 描述符。15 条legacy 3 + 新 8 + Scope B 4续写/扩写/降AI/拆书)。 # 创作工具箱注册表key → 描述符。15 条legacy 3 + 新 8 + Scope B 4续写/扩写/降AI/拆书)。
TOOLBOX: dict[str, GeneratorTool] = { _TOOLBOX_BY_KEY: dict[str, GeneratorTool] = {
# ---- legacyspec=None前端走 legacy_route 跳现有页面)---- # ---- legacyspec=None前端走 legacy_route 跳现有页面)----
"worldbuilding": GeneratorTool( "worldbuilding": GeneratorTool(
key="worldbuilding", key="worldbuilding",
@@ -243,6 +247,10 @@ TOOLBOX: dict[str, GeneratorTool] = {
), ),
} }
# MappingProxyType只读视图运行时 `TOOLBOX[x]=...` / `del TOOLBOX[x]` 抛 TypeError单一
# 真相源不可被污染;`Final` 仅静态检查、挡不住运行时变异,故须冻结成只读 Mapping
TOOLBOX: Final[Mapping[str, GeneratorTool]] = MappingProxyType(_TOOLBOX_BY_KEY)
def get_tool(key: str) -> GeneratorTool | None: def get_tool(key: str) -> GeneratorTool | None:
"""按 key 取生成器描述符;未知 key → None端点据此返 404""" """按 key 取生成器描述符;未知 key → None端点据此返 404"""