实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
332 lines
15 KiB
Python
332 lines
15 KiB
Python
"""真实确认声音进入写手请求;合成HTTP只验证工程链路,不证明文学收益。"""
|
||
|
||
import json
|
||
from uuid import uuid4
|
||
|
||
import pytest
|
||
import test_两阶段写手 as 写手测试
|
||
from test_两阶段写手 import 发起并跑完, 正常剧本
|
||
|
||
from muse.上下文.接口 import 上下文错误, 保护模型发送, 核对依赖, 装配材料
|
||
from muse.任务运行.接口 import 任务状态
|
||
from muse.审校修订.接口 import 声音定位, 声音提案请求, 声音样张引用
|
||
from muse.正文写作.接口 import 候选定位
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
生成环境 = 写手测试.生成环境
|
||
|
||
|
||
def 确认本书声音(env, *, 确认=True, narrator=None):
|
||
服务, 作者 = env["装配"].要求审校(), env["作者"]
|
||
当前 = 服务.当前声音(作者, "gen-work")
|
||
文 = env["正文"].读取正文(作者, "ch-1")
|
||
请求 = 声音提案请求(
|
||
"gen-work",
|
||
当前["revision"] if 当前 else 0,
|
||
"style",
|
||
1,
|
||
narrator
|
||
or {"句式画像": "长短交替,不替人物解释感受。", "达标样张": "林深停在码头,雨还没停。"},
|
||
(声音样张引用("document", "ch-1", 1, 文["document_hash"], "main"),),
|
||
verbal_tics=("雨还没停",),
|
||
protected_expressions=("林深停在码头",),
|
||
)
|
||
提案 = 服务.提出声音(作者, "voice-" + str(uuid4()), 请求)["results"][0]
|
||
定位 = 声音定位(
|
||
"gen-work", 提案["proposal_id"], 1, 提案["proposal_hash"], 请求.expected_voice_revision
|
||
)
|
||
if 确认:
|
||
审阅 = 服务.打开声音审阅(作者, 定位)
|
||
服务.决定声音(
|
||
作者,
|
||
"voice-adopt-" + str(uuid4()),
|
||
定位,
|
||
审阅ID=审阅["review_id"],
|
||
审阅哈希=审阅["review_hash"],
|
||
)
|
||
return 定位
|
||
|
||
|
||
def 读取材料(env, task):
|
||
点 = next(s for s in task.步骤 if s["step_id"] == "冻结回放")["checkpoint"]
|
||
return env["装配"].要求上下文().读取快照(点["快照ID"])
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d001",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="真实确认声音、S04和合成写手",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["模型实际输入与B09逐键声音材料一致;真实版本绑定;错放材料不能冻结"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_实际写作请求含真实声音及版本且不能错放材料__22d001(生成环境):
|
||
env = 生成环境
|
||
确认本书声音(env)
|
||
tid, task = 发起并跑完(env, 剧本=正常剧本(["林深停在码头,门后有人应声。"]))
|
||
assert task.状态 == 任务状态.已完成, task.步骤
|
||
快照 = 读取材料(env, task)
|
||
声音 = 快照.冻结范围["voice"]
|
||
assert 声音["revision"] == 1 and 声音["schema_hash"]
|
||
assert any(
|
||
b.来源ID == 声音["version_id"] and b.投影版本 == 声音["projection_version"]
|
||
for b in 快照.来源绑定
|
||
)
|
||
原材料 = {k: 快照.装配["文本"][a:b] for k, a, b in 快照.装配["材料区间"]}
|
||
声键 = 声音["材料键"]
|
||
指导 = json.loads(原材料[声键])
|
||
assert 指导["narrator"]["句式画像"] == "长短交替,不替人物解释感受。"
|
||
assert 指导["protected_expressions"] == ["林深停在码头"]
|
||
assert "samples" not in 指导
|
||
assert json.loads(env["收到"][-1]["input"])["装配材料"] == 快照.装配["文本"]
|
||
原材料["目标章细纲"], 原材料[声键] = 原材料[声键], 原材料["目标章细纲"]
|
||
with pytest.raises(上下文错误, match="声音"):
|
||
env["装配"].要求上下文().冻结上下文(
|
||
tid, 装配材料(原材料, ("目标章细纲", 声键), 字节上限=快照.装配["字节数"]), 快照.来源绑定
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d002",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="仅有未确认声音候选",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["空基线固定为null,没有指导材料或声音正文进入模型"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_未确认声音不进入写手且空基线也固定__22d002(生成环境):
|
||
env = 生成环境
|
||
确认本书声音(env, 确认=False)
|
||
_, task = 发起并跑完(env, 剧本=正常剧本(["林深停在码头,门后有人应声。"]))
|
||
assert task.状态 == 任务状态.已完成
|
||
快照 = 读取材料(env, task)
|
||
assert "voice" in 快照.冻结范围 and 快照.冻结范围["voice"] is None
|
||
assert all(not k.startswith("声音:") for k in 快照.装配["入选"])
|
||
assert "长短交替,不替人物解释感受。" not in env["收到"][-1]["input"]
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d003",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="已有声音/无声音两种基线,冻结后确认新版本",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["写作发送、依赖重验和发送短事务均拒旧依据,旧快照标SOURCE_STALE"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
@pytest.mark.parametrize("已有", [True, False])
|
||
def test_冻结后声音变化阻写作发送并标旧快照失效__22d003(生成环境, 已有):
|
||
env = 生成环境
|
||
if 已有:
|
||
确认本书声音(env)
|
||
|
||
任务ID = []
|
||
|
||
def 换声音(tid):
|
||
任务ID.append(tid)
|
||
确认本书声音(env, narrator={"句式画像": "作者改为更克制的指导。"})
|
||
|
||
from muse.编排.生成正文 import 生成正文错误
|
||
|
||
with pytest.raises(生成正文错误, match="声音"):
|
||
发起并跑完(env, 剧本=正常剧本(["林深停在码头。"]), 冻结后=换声音)
|
||
tid = 任务ID[0]
|
||
task = env["装配"].任务运行.读取任务(tid)
|
||
assert task.状态 == 任务状态.已失败
|
||
assert len(env["收到"]) == 4, "声音失效不能再发送写作回合"
|
||
快照 = 读取材料(env, task)
|
||
assert 快照.当前状态 == "SOURCE_STALE" and "声音" in 快照.失效说明
|
||
with env["库"].连接(只读=True) as 连:
|
||
状态 = 核对依赖(连, 快照.冻结范围)
|
||
assert not 状态.有效 and any("声音" in x for x in 状态.变化项)
|
||
with env["库"].连接() as 连, 连.transaction(), pytest.raises(上下文错误, match="声音"):
|
||
保护模型发送(
|
||
连,
|
||
env["库"].用途,
|
||
env["作者"].作者,
|
||
tid,
|
||
快照.快照ID,
|
||
json.dumps({"上下文快照ID": 快照.快照ID, "装配材料": 快照.装配["文本"]}),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d004",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="真实模型交付及作者审阅后再修改声音",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["不可变历史材料保留;采纳复检拒旧声音,不写正文"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_交付后声音变化不篡改历史但拒旧候选采纳__22d004(生成环境):
|
||
env = 生成环境
|
||
确认本书声音(env)
|
||
_, task = 发起并跑完(env, 剧本=正常剧本(["林深停在码头,门后有人应声。"]))
|
||
assert task.状态 == 任务状态.已完成
|
||
快照 = 读取材料(env, task)
|
||
保存 = next(s for s in task.步骤 if s["step_id"] == "检查与候选")["checkpoint"]["候选"]
|
||
c = env["正文"].读取候选(env["作者"], 保存["candidate_id"])
|
||
定位 = 候选定位(c["candidate_id"], c["revision"], c["candidate_hash"], c["document_id"], 0)
|
||
审阅 = env["正文"].打开候选审阅(env["作者"], 定位)
|
||
确认本书声音(env, narrator={"句式画像": "改变后的指导。"})
|
||
from muse.正式变更.接口 import 作者动作
|
||
|
||
with pytest.raises(上下文错误, match="声音"):
|
||
env["正文"].决定候选(
|
||
env["作者"],
|
||
"adopt-stale-voice",
|
||
定位,
|
||
作者动作.采纳,
|
||
审阅["review_id"],
|
||
审阅["review_hash"],
|
||
("document",),
|
||
)
|
||
后 = env["装配"].要求上下文().读取快照(快照.快照ID)
|
||
assert 后.装配 == 快照.装配 and 后.来源绑定 == 快照.来源绑定
|
||
assert env["正文"].读取正文(env["作者"], "ch-3")["revision"] == 0
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d005",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="S04仅禁止generation字段而保留detection",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["按任务用途真投影;旧声音投影拒绝继续消费,留存内容哈希不变"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_声音投影随任务用途且策略收紧阻旧发送__22d005(生成环境):
|
||
from dataclasses import asdict, replace
|
||
|
||
from muse.上下文.接口 import 核对范围声音
|
||
from muse.元数据.接口 import 元数据服务, 启用命令, 字段限制, 定义哈希
|
||
from muse.共享.调用身份 import 内容用途, 用途
|
||
|
||
env = 生成环境
|
||
应用测试库 = env["库组"]
|
||
确认本书声音(env)
|
||
上下文 = env["装配"].要求上下文()
|
||
旧 = 上下文.授权任务范围(env["作者"], "gen-work", 2, 含目录=True, 含声音=True)
|
||
with 应用测试库[用途.维护].连接() as 连, 连.transaction():
|
||
元数据 = 元数据服务(连)
|
||
当前 = 元数据.当前策略("style")
|
||
新 = replace(
|
||
当前,
|
||
version=当前.version + 1,
|
||
限制=(*当前.限制, 字段限制("style.句式画像", ("aiContext:generation",))),
|
||
)
|
||
元数据.更新策略(
|
||
新,
|
||
启用命令(
|
||
"voice-restrict", 定义哈希(asdict(新)), 当前.version, env["作者"].作者, "type:style"
|
||
),
|
||
)
|
||
with env["库"].连接(只读=True) as 连, pytest.raises(上下文错误, match="声音"):
|
||
核对范围声音(连, 旧)
|
||
服务 = env["装配"].要求审校()
|
||
新声 = 服务.当前声音(env["作者"], "gen-work", 字段用途="aiContext")
|
||
检测声 = 服务.当前声音(
|
||
replace(env["作者"], 内容用途=内容用途.检测), "gen-work", 字段用途="aiContext"
|
||
)
|
||
assert "句式画像" not in 新声["narrator"] and "句式画像" in 检测声["narrator"]
|
||
assert 新声["projection_version"] != 检测声["projection_version"]
|
||
assert 新声["content_hash"] == 检测声["content_hash"] == 旧["voice"]["content_hash"]
|
||
assert 新声["revision"] == 1
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d006",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="大声音材料超出写手字节预算",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["必读不足拒冻结与写作,不静默省略或截断声音"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_声音必读不能被预算省略__22d006(生成环境):
|
||
from muse.编排.生成正文 import 发起生成正文
|
||
|
||
env = 生成环境
|
||
确认本书声音(env, narrator={"句式画像": "超出本轮预算的声音指导。" * 10000})
|
||
env["剧本"].extend(正常剧本(["不应发送的正文"]))
|
||
回执 = 发起生成正文(
|
||
env["装配"],
|
||
env["作者"],
|
||
"voice-over-budget",
|
||
"new_chapter",
|
||
work_id="gen-work",
|
||
chapter_id="ch-3",
|
||
配置ID="gen-config",
|
||
)
|
||
from datetime import UTC, datetime, timedelta
|
||
from decimal import Decimal
|
||
|
||
from muse.任务运行.预算管理 import 任务预算计划, 角色预算, 预算管理, 额度策略
|
||
|
||
预算 = 预算管理(env["库"], "synthetic")
|
||
预算.登记策略(额度策略("synthetic", "1", Decimal("20"), 40))
|
||
预算.登记任务预算(
|
||
回执["task_id"],
|
||
任务预算计划(
|
||
Decimal("10"),
|
||
(角色预算("writer", 8, 8, Decimal("1")),),
|
||
"approval",
|
||
datetime.now(UTC) + timedelta(minutes=10),
|
||
),
|
||
)
|
||
运行 = env["装配"].任务运行
|
||
with pytest.raises(上下文错误) as 错误:
|
||
while True:
|
||
领取 = 运行.领取步骤("worker", 写手测试.处理器能力)
|
||
assert 领取 is not None
|
||
运行.执行一步(领取)
|
||
assert 错误.value.错误码 == "CONTEXT_BUDGET_EXCEEDED"
|
||
assert len(env["收到"]) == 4
|
||
assert env["装配"].要求上下文().读取任务快照(回执["task_id"]) == ()
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w22-22d007",
|
||
environment="隔离PG与合成HTTP提供方",
|
||
given="尚无声音时的发送保护及两份并发确认",
|
||
when="经真实声音确认、任务范围、冻结快照、合成HTTP写作或采纳公共用例",
|
||
then=["null也持作品锁,两份真实确认只有一个成功,不覆盖先行版本"],
|
||
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md",
|
||
)
|
||
def test_首次声音确认与发送保护同锁且两确认只有一个成功__22d007(生成环境):
|
||
from concurrent.futures import ThreadPoolExecutor
|
||
from threading import Barrier
|
||
|
||
from psycopg.errors import LockNotAvailable
|
||
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.审校修订.接口 import 读取确认声音
|
||
|
||
env = 生成环境
|
||
# null也锁定本书,另一个事务不能绕过首次确认的排序。
|
||
with env["库"].连接() as 一, 一.transaction():
|
||
assert 读取确认声音(一, env["作者"].作者, "gen-work", 保护到事务结束=True) is None
|
||
with env["库"].连接() as 二, 二.transaction(), pytest.raises(LockNotAvailable):
|
||
二.execute("SET LOCAL lock_timeout = '150ms'")
|
||
读取确认声音(二, env["作者"].作者, "gen-work", 保护到事务结束=True)
|
||
服务 = env["装配"].要求审校()
|
||
定位 = [确认本书声音(env, 确认=False) for _ in range(2)]
|
||
审阅 = [服务.打开声音审阅(env["作者"], 条) for 条 in 定位]
|
||
栅栏 = Barrier(2)
|
||
|
||
def 采纳(i):
|
||
栅栏.wait(timeout=5)
|
||
try:
|
||
return 服务.决定声音(
|
||
env["作者"],
|
||
"voice-concurrent-" + str(i),
|
||
定位[i],
|
||
审阅ID=审阅[i]["review_id"],
|
||
审阅哈希=审阅[i]["review_hash"],
|
||
)
|
||
except Muse错误 as e:
|
||
return e
|
||
|
||
with ThreadPoolExecutor(2) as 池:
|
||
结果 = list(池.map(采纳, range(2)))
|
||
assert sum(isinstance(r, dict) for r in 结果) == 1
|
||
assert sum(isinstance(r, Muse错误) for r in 结果) == 1
|
||
assert 服务.当前声音(env["作者"], "gen-work")["revision"] == 1
|