实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
346 lines
13 KiB
Python
346 lines
13 KiB
Python
"""世界设定维护、人物弧线分离与修订影响:S04 族驱动、计划/已发生分开、影响只标记不撤回。"""
|
||
|
||
import pytest
|
||
from pydantic import TypeAdapter
|
||
|
||
from muse.作品规划.接口 import 档案保存
|
||
from muse.元数据.接口 import 元数据服务
|
||
from muse.共享.调用身份 import 内容用途, 用途, 调用身份
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.启动 import 构建
|
||
from muse.故事世界.接口 import 事实定位, 事实提案请求, 叙事窗口, 定位字面引文, 正文依据
|
||
from muse.正式变更.接口 import 作者动作
|
||
from muse.正文写作.接口 import 文本节点, 正文草稿, 段落
|
||
from muse.配置 import 应用配置
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
|
||
|
||
@pytest.fixture
|
||
def 世界环境(内置结构测试库):
|
||
应用测试库 = 内置结构测试库
|
||
装配 = 构建(应用配置(应用测试库[用途.生产].引用, "synthetic"))
|
||
with 装配.生命周期():
|
||
作者 = 调用身份("author", None, 用途.生产, 内容用途.规划)
|
||
作品 = 装配.要求作品()
|
||
结构 = 作品.新档案结构(作者, "story-work", "work_core", 1)
|
||
作品.保存档案(
|
||
作者, "create-story", 档案保存("story-work", 0, {"名称": "合成故事世界"}, 结构)
|
||
)
|
||
for i in range(1, 4):
|
||
作品.添加章节(
|
||
作者, f"chapter-{i}", "story-work", f"chapter-{i}", f"第{i}幕", 预期目录版本=i - 1
|
||
)
|
||
正文 = 装配.要求正文()
|
||
for i, text in [
|
||
(1, "林深停在码头,何岚站在船旁。"),
|
||
(2, "何岚知道林深已经登船。"),
|
||
(3, "林深来到北岸。"),
|
||
]:
|
||
正文.保存人工(
|
||
作者, f"body-{i}", f"chapter-{i}", 0, 正文草稿((段落(f"p{i}", (文本节点(text),)),))
|
||
)
|
||
yield 装配, 作者, 应用测试库
|
||
|
||
|
||
def 来源(装配, 作者, chapter, quote):
|
||
记录 = 装配.要求正文().读取正文(作者, chapter)
|
||
原文 = TypeAdapter(正文草稿).validate_python(记录["document"])
|
||
return 正文依据(
|
||
chapter, "main", 记录["revision"], 记录["document_hash"], (定位字面引文(原文, quote),)
|
||
)
|
||
|
||
|
||
def 确认提案(装配, 作者, 回执, object_id, expected=0, command="decide"):
|
||
世界 = 装配.要求故事世界()
|
||
proposal_id = 回执["results"][0]["proposal_id"]
|
||
记录 = 世界.读取提案(作者, proposal_id)
|
||
定位 = 事实定位(
|
||
proposal_id, 记录["revision"], 记录["proposal_hash"], "world:" + object_id, expected
|
||
)
|
||
审阅 = 世界.打开事实审阅(作者, 定位)
|
||
return 世界.决定事实(
|
||
作者,
|
||
f"{command}-{proposal_id[:8]}",
|
||
定位,
|
||
作者动作.采纳,
|
||
审阅["review_id"],
|
||
审阅["review_hash"],
|
||
("object",),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19a01a",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="新类型零代码经世界设定编辑",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_新类型零代码经世界设定编辑__19a01a(世界环境):
|
||
from muse.元数据.接口 import 值规则, 字段定义, 类型定义, 结构定义
|
||
|
||
装配, 作者, 库 = 世界环境
|
||
with 库[用途.维护].连接() as 连, 连.transaction():
|
||
元数据 = 元数据服务(连)
|
||
元数据.登记类型(类型定义("cost_rule", "代价规则", "world", "entity", ("entity",)))
|
||
结构 = 结构定义(
|
||
"cost_rule",
|
||
"cost_rule",
|
||
1,
|
||
(
|
||
字段定义("cost.name", "名称", "名称", 值规则("text"), 必填=True),
|
||
字段定义("cost.price", "代价", "代价", 值规则("text")),
|
||
),
|
||
)
|
||
元数据.登记结构候选(结构)
|
||
元数据.发布结构("cost_rule", 1, 结构.内容哈希)
|
||
世界 = 装配.要求故事世界()
|
||
# 世界设定 save_entity:S04 新类型不改生产代码即可提案→确认→列出。
|
||
回执 = 世界.保存实体提案(
|
||
作者,
|
||
"save-cost-1",
|
||
work_id="story-work",
|
||
object_id="cost-sea",
|
||
expected_revision=0,
|
||
schema_id="cost_rule",
|
||
schema_version=1,
|
||
content={"名称": "渡海代价", "代价": "一年记忆"},
|
||
)
|
||
确认提案(装配, 作者, 回执, "cost-sea")
|
||
with 装配.数据库.连接(只读=True) as 连:
|
||
from muse.故事世界.世界设定 import 世界类型清单, 列出实体
|
||
|
||
实体 = [e for e in 列出实体(连, "author", "story-work") if e["object_id"] == "cost-sea"]
|
||
assert len(实体) == 1 and 实体[0]["type_id"] == "cost_rule"
|
||
assert 实体[0]["knowledge_mode"] == "setting" and 实体[0]["independent"]
|
||
assert any(t["type_id"] == "cost_rule" for t in 世界类型清单(连))
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19a02b",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="实体轨迹版本追查与旧版本保留",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_实体轨迹版本追查与旧版本保留__19a02b(世界环境):
|
||
装配, 作者, _ = 世界环境
|
||
世界 = 装配.要求故事世界()
|
||
r1 = 世界.保存实体提案(
|
||
作者,
|
||
"save-char-1",
|
||
work_id="story-work",
|
||
object_id="char-lin",
|
||
expected_revision=0,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "主角"},
|
||
)
|
||
确认提案(装配, 作者, r1, "char-lin")
|
||
r2 = 世界.保存实体提案(
|
||
作者,
|
||
"save-char-2",
|
||
work_id="story-work",
|
||
object_id="char-lin",
|
||
expected_revision=1,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "重要配角"},
|
||
)
|
||
确认提案(装配, 作者, r2, "char-lin", expected=1)
|
||
轨迹 = 世界.读取实体轨迹(作者, "story-work", "char-lin")
|
||
assert [v["revision"] for v in 轨迹["版本"]] == [2, 1]
|
||
assert 轨迹["版本"][1]["content"]["身份定位"] == "主角" # 旧版本不抹
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19a03c",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="修订影响只标记不撤回独立确认",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_修订影响只标记不撤回独立确认__19a03c(世界环境):
|
||
装配, 作者, _ = 世界环境
|
||
世界 = 装配.要求故事世界()
|
||
# 独立设定:何岚的人物卡(不依赖正文)。
|
||
r = 世界.保存实体提案(
|
||
作者,
|
||
"save-he-1",
|
||
work_id="story-work",
|
||
object_id="char-he",
|
||
expected_revision=0,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "重要配角"},
|
||
)
|
||
确认提案(装配, 作者, r, "char-he")
|
||
# 林深先确认 r1,再做未决的 r2 修订提案。
|
||
r1 = 世界.保存实体提案(
|
||
作者,
|
||
"save-lin-1",
|
||
work_id="story-work",
|
||
object_id="char-lin",
|
||
expected_revision=0,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "主角"},
|
||
)
|
||
确认提案(装配, 作者, r1, "char-lin")
|
||
世界.保存实体提案(
|
||
作者,
|
||
"save-lin-2",
|
||
work_id="story-work",
|
||
object_id="char-lin",
|
||
expected_revision=1,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "重要配角"},
|
||
)
|
||
# 何岚无未决修订;林深的评估列出自己的待决 r2。
|
||
影响 = 世界.评估修订影响(作者, "story-work", "char-he")
|
||
assert 影响["object_id"] == "char-he" and 影响["本对象待决提案"] == []
|
||
影响2 = 世界.评估修订影响(作者, "story-work", "char-lin")
|
||
assert any(p["object_id"] == "char-lin" for p in 影响2["本对象待决提案"])
|
||
# 独立确认不受影响标记撤回:何岚仍可用。
|
||
with 装配.数据库.连接(只读=True) as 连:
|
||
from muse.故事世界.世界设定 import 列出实体
|
||
|
||
实体 = {e["object_id"]: e for e in 列出实体(连, "author", "story-work")}
|
||
assert 实体["char-he"]["依据可用"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19a04d",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="章序变化影响列出叙事位置事实",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_章序变化影响列出叙事位置事实__19a04d(世界环境):
|
||
装配, 作者, _ = 世界环境
|
||
世界 = 装配.要求故事世界()
|
||
依据 = (来源(装配, 作者, "chapter-2", "何岚知道林深已经登船"),)
|
||
结构 = 世界.新事实结构(作者, "story-work", "char-lin", "character", 1)
|
||
请求 = 事实提案请求(
|
||
"story-work",
|
||
"char-lin",
|
||
0,
|
||
结构,
|
||
{"身份定位": "主角"},
|
||
依据,
|
||
"chapter-2",
|
||
planned_window=叙事窗口("chapter-2", "chapter-3"),
|
||
)
|
||
回执 = 世界.提出事实(作者, "propose-lin", 请求)
|
||
确认提案(装配, 作者, 回执, "char-lin")
|
||
新章序 = {"chapter-1": 1, "chapter-2": 3, "chapter-3": 2}
|
||
with 装配.数据库.连接(只读=True) as 连:
|
||
from muse.故事世界.设定修订 import 评估修订影响
|
||
|
||
影响 = 评估修订影响(连, "author", "story-work", "char-lin", 新章序=新章序)
|
||
涉及对象 = {项["object_id"]: 项["涉及章"] for 项 in 影响["章序影响"]}
|
||
assert "char-lin" in 涉及对象 and "chapter-2" in 涉及对象["char-lin"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19b01e",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="人物弧线计划与已发生分开",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_人物弧线计划与已发生分开__19b01e(世界环境):
|
||
装配, 作者, _ = 世界环境
|
||
世界 = 装配.要求故事世界()
|
||
r1 = 世界.保存实体提案(
|
||
作者,
|
||
"arc-plan-1",
|
||
work_id="story-work",
|
||
object_id="char-gu",
|
||
expected_revision=0,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "反派"},
|
||
knowledge_mode="plan",
|
||
)
|
||
确认提案(装配, 作者, r1, "char-gu")
|
||
r2 = 世界.保存实体提案(
|
||
作者,
|
||
"arc-fact-1",
|
||
work_id="story-work",
|
||
object_id="char-gu",
|
||
expected_revision=1,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "反派"},
|
||
knowledge_mode="fact",
|
||
)
|
||
确认提案(装配, 作者, r2, "char-gu", expected=1)
|
||
弧线 = 世界.人物弧线(作者, "story-work", "char-gu")
|
||
assert [v["knowledge_mode"] for v in 弧线["计划"]] == ["plan"]
|
||
assert [v["knowledge_mode"] for v in 弧线["已发生"]] == ["fact"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w19-19b02f",
|
||
environment="真实隔离 PG",
|
||
given="隔离数据库与 S04 登记结构",
|
||
when="关系端点解析为本书对象",
|
||
then=["行为可复验", "先审后入", "修订不撤回独立确认"],
|
||
contract="docs/系统架构/新版设计/模块设计/B02-故事世界.md",
|
||
)
|
||
def test_关系端点解析为本书对象__19b02f(世界环境):
|
||
装配, 作者, _ = 世界环境
|
||
世界 = 装配.要求故事世界()
|
||
for 对象 in ("char-lin", "char-he"):
|
||
r = 世界.保存实体提案(
|
||
作者,
|
||
f"rel-{对象}",
|
||
work_id="story-work",
|
||
object_id=对象,
|
||
expected_revision=0,
|
||
schema_id="character",
|
||
schema_version=1,
|
||
content={"身份定位": "主角"},
|
||
)
|
||
确认提案(装配, 作者, r, 对象)
|
||
依据 = (来源(装配, 作者, "chapter-1", "林深停在码头,何岚站在船旁"),)
|
||
结构 = 世界.新事实结构(作者, "story-work", "rel-1", "character_relation", 1, 家族="relation")
|
||
请求 = 事实提案请求(
|
||
"story-work",
|
||
"rel-1",
|
||
0,
|
||
结构,
|
||
{"甲方": "char-lin", "乙方": "char-he", "关系类型": "同船"},
|
||
依据,
|
||
"chapter-1",
|
||
instance_family="relation",
|
||
)
|
||
回执 = 世界.提出事实(作者, "propose-rel", 请求)
|
||
确认提案(装配, 作者, 回执, "rel-1")
|
||
视图 = 世界.人物关系视图(作者, "story-work")
|
||
关系 = [r for r in 视图["关系"] if r["object_id"] == "rel-1"]
|
||
assert len(关系) == 1
|
||
端点集 = {端["object_id"] for 端 in 关系[0]["端点"]}
|
||
assert 端点集 == {"char-lin", "char-he"}
|
||
# 端点必须是已解析的本书对象:不存在的端点在提案校验即被拒。
|
||
坏请求 = 事实提案请求(
|
||
"story-work",
|
||
"rel-2",
|
||
0,
|
||
结构,
|
||
{"甲方": "char-none", "乙方": "char-he", "关系类型": "无据"},
|
||
依据,
|
||
"chapter-1",
|
||
instance_family="relation",
|
||
)
|
||
with pytest.raises(Muse错误):
|
||
世界.提出事实(作者, "propose-bad-rel", 坏请求)
|