实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
197 lines
8.1 KiB
Python
197 lines
8.1 KiB
Python
"""真实任务尝试经CLI/HTTP记录观察;不向请求方披露原始输入或派发模型。"""
|
||
|
||
import json
|
||
import os
|
||
import socket
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
from dataclasses import replace
|
||
from pathlib import Path
|
||
from threading import Thread
|
||
|
||
import httpx
|
||
import pytest
|
||
import test_生产评测权限隔离 as 接入测试
|
||
import test_运行观察与来源 as 观察测试
|
||
import uvicorn
|
||
from fastapi.testclient import TestClient
|
||
|
||
from muse.接入.http.应用 import 创建应用
|
||
from muse.资源加载 import 加载清单
|
||
from muse.配置 import 读取配置
|
||
|
||
pytestmark = pytest.mark.数据库
|
||
观察环境 = 观察测试.观察环境
|
||
|
||
|
||
def _配置(env, tmp_path):
|
||
path = 接入测试._配置文件(env["pool"], tmp_path)
|
||
path.write_text(
|
||
path.read_text()
|
||
.replace("eval-author", env["author"].作者)
|
||
.replace(
|
||
'"发布身份"="test"',
|
||
'"发布身份"=' + json.dumps(加载清单()["构建身份"]),
|
||
)
|
||
)
|
||
return path
|
||
|
||
|
||
def _cli(tmp_path, config, action, *args):
|
||
response = subprocess.run(
|
||
[sys.executable, "-I", "-m", "muse", "经验", str(config), action, *map(str, args)],
|
||
cwd=tmp_path,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=30,
|
||
)
|
||
assert response.returncode == 0, response.stderr
|
||
return json.loads(response.stdout)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w26-26e101",
|
||
environment="隔离PG/合成HTTP及浏览器",
|
||
given="固定请求、来源与作者;按用例环境准备独立测试输入",
|
||
when="通过实际公开入口执行并核对拒绝、状态及持久结果",
|
||
then=["真实CLI及HTTP按原任务用途保存观察且读取不重发"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察",
|
||
)
|
||
def test_真实CLI及HTTP按原任务用途保存观察且读取不重发__26e101(观察环境, tmp_path):
|
||
env = 观察环境
|
||
task_id, claim, _ = 观察测试._完成(env)
|
||
before = env["run"].读取任务(task_id)
|
||
config = _配置(env, tmp_path)
|
||
source_request = {"task_id": task_id, "attempt_id": claim.尝试ID}
|
||
source_file = tmp_path / "source.json"
|
||
source_file.write_text(json.dumps(source_request))
|
||
source = _cli(tmp_path, config, "运行来源", source_file)
|
||
assert source["runtime"]["content_purpose"] == "generation"
|
||
assert "不应进入运行观察的正文" not in json.dumps(source, ensure_ascii=False)
|
||
body = {
|
||
"command_id": "entry-observation",
|
||
"observation": {
|
||
"sources": [source["locator"]],
|
||
"phenomenon": "实际尝试有结果",
|
||
"quote": "保留这次的观察。",
|
||
"background": "固定合成任务。",
|
||
"issue_key": "同类结果",
|
||
},
|
||
}
|
||
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
|
||
assert (
|
||
client.post(
|
||
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
|
||
).status_code
|
||
== 200
|
||
)
|
||
task = client.get("/api/v1/tasks/" + task_id)
|
||
assert task.status_code == 200 and task.json()["steps"][0]["attempt_id"] == claim.尝试ID
|
||
reread = client.post("/api/v1/run-observation-sources", json=source_request)
|
||
assert reread.status_code == 200 and reread.json() == source
|
||
assert client.get("/api/v1/run-observations", params={"task_id": task_id}).json() == []
|
||
bad = {**body, "observation": {**body["observation"], "state": "promoted"}}
|
||
assert client.post("/api/v1/run-observations", json=bad).status_code == 422
|
||
saved = client.post("/api/v1/run-observations", json=body)
|
||
assert saved.status_code == 201, saved.text
|
||
oid = saved.json()["observation_id"]
|
||
assert (
|
||
client.get("/api/v1/run-observations/" + oid).json()["payload"]["quote"]
|
||
== "保留这次的观察。"
|
||
)
|
||
repeats = client.get("/api/v1/recurring-issues", params={"issue_key": "同类结果"})
|
||
assert repeats.status_code == 200, repeats.text
|
||
write_file = tmp_path / "observation.json"
|
||
write_file.write_text(json.dumps(body))
|
||
replay = _cli(tmp_path, config, "记录运行观察", write_file)
|
||
assert replay["deduped"] and replay["observation_id"] == oid
|
||
assert (
|
||
_cli(tmp_path, config, "运行观察", oid)["payload"]["sources"][0]["runtime"]["attempt_id"]
|
||
== claim.尝试ID
|
||
)
|
||
assert len(_cli(tmp_path, config, "运行观察清单")) == 1
|
||
assert env["run"].读取任务(task_id) == before
|
||
other = tmp_path / "other.toml"
|
||
other.write_text(config.read_text().replace(env["author"].作者, "other-observer"))
|
||
with TestClient(创建应用(读取配置(other)), headers={"Origin": "http://testserver"}) as client:
|
||
client.post("/api/v1/session", json={"password": "synthetic-evaluation-only"})
|
||
assert (
|
||
client.post("/api/v1/run-observation-sources", json=source_request).status_code == 403
|
||
)
|
||
assert client.get("/api/v1/run-observations").json() == []
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-w26-26e102",
|
||
environment="隔离PG/合成HTTP及浏览器",
|
||
given="固定请求、来源与作者;按用例环境准备独立测试输入",
|
||
when="通过实际公开入口执行并核对拒绝、状态及持久结果",
|
||
then=["任务页真实执行观察保存刷新且运行本身不变"],
|
||
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#有来源的运行观察",
|
||
)
|
||
@pytest.mark.浏览器
|
||
def test_任务页真实执行观察保存刷新且运行本身不变__26e102(观察环境, tmp_path):
|
||
if os.environ.get("MUSE_RUN_BROWSER") != "1":
|
||
pytest.skip("需显式启用浏览器")
|
||
env = 观察环境
|
||
task_id, claim, _ = 观察测试._完成(env)
|
||
before = env["run"].读取任务(task_id)
|
||
config_file = _配置(env, tmp_path)
|
||
with socket.socket() as sock:
|
||
sock.bind(("127.0.0.1", 0))
|
||
port = sock.getsockname()[1]
|
||
url = f"http://127.0.0.1:{port}"
|
||
config = 读取配置(config_file)
|
||
assert config.HTTP is not None
|
||
config = replace(config, HTTP=replace(config.HTTP, 公开地址=url, 允许来源=(url,)))
|
||
server = uvicorn.Server(
|
||
uvicorn.Config(创建应用(config), host="127.0.0.1", port=port, log_level="warning")
|
||
)
|
||
thread = Thread(target=server.run, daemon=True)
|
||
thread.start()
|
||
output = Path(os.environ.get("MUSE_BROWSER_ARTIFACTS", str(tmp_path / "browser"))).resolve()
|
||
output.mkdir(parents=True)
|
||
try:
|
||
for _ in range(100):
|
||
if server.started:
|
||
break
|
||
time.sleep(0.05)
|
||
assert server.started and httpx.get(url + "/ready", trust_env=False).status_code == 200
|
||
environment = {
|
||
**os.environ,
|
||
"MUSE_WORKBENCH_URL": url,
|
||
"MUSE_AUTHOR_PASSWORD_FILE": config.HTTP.口令文件,
|
||
"MUSE_OBSERVATION_TASK": task_id,
|
||
"MUSE_OBSERVATION_ATTEMPT": claim.尝试ID,
|
||
"MUSE_BROWSER_EXECUTABLE": (
|
||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||
),
|
||
}
|
||
with (output / "浏览器.json").open("w") as report:
|
||
process = subprocess.run(
|
||
[
|
||
"pnpm",
|
||
"exec",
|
||
"playwright",
|
||
"test",
|
||
"运行观察.spec.ts",
|
||
"--reporter=json",
|
||
"--output",
|
||
str(output / "产物"),
|
||
],
|
||
cwd=Path(__file__).resolve().parents[2] / "web",
|
||
env=environment,
|
||
stdout=report,
|
||
stderr=subprocess.STDOUT,
|
||
timeout=90,
|
||
)
|
||
assert process.returncode == 0, "浏览器未通过,见浏览器.json"
|
||
rows = env["service"].列出运行观察(env["author"], 任务ID=task_id)
|
||
assert len(rows) == 1 and rows[0]["payload"]["quote"] == "先把这次的现象记下来。"
|
||
assert env["run"].读取任务(task_id) == before
|
||
finally:
|
||
server.should_exit = True
|
||
thread.join(10)
|
||
assert not thread.is_alive()
|