Files
AgentLab/backend/tests/test_tool_node_snapshot_owner.py

70 lines
2.9 KiB
Python

"""调用工具节点作证的第二道(D-p,同 nodes/llm.py):名字是平台执行的查询还不够,非数据源的工具交回的工件还得是这次
运行落下的端点快照(capabilities.registry.snapshot_owned)。拼一个同样形状的 JSON、塞别的工件 id,不进台账、tool.end
上也不带 query_artifact。
"""
from __future__ import annotations
import asyncio
import pytest
from sqlalchemy import select
from app.db.base import SessionLocal
from app.db.models import Run, RunEvent
from app.engine.nodes import tools as tool_node
from app.engine.runner import run_manager
@pytest.fixture(autouse=True)
async def engine_up():
await run_manager.setup()
yield
await run_manager.shutdown()
def node(nid, ntype, **config):
return {"id": nid, "type": ntype, "position": {"x": 0, "y": 0}, "data": {"label": nid, "config": config}}
GRAPH = {"nodes": [node("start", "input"), node("read", "tool", tool="calculator", args={"expression": "1+1"}),
node("out", "output", fields=[{"name": "r", "value": "完成"}])],
"edges": [{"source": "start", "target": "read"}, {"source": "read", "target": "out"}]}
FORGED = {"artifact": "f" * 64, "source": "erp", "columns": ["金额"], "rows": [[999]], "truncated": False,
"request": {}}
async def _tool_end(monkeypatch, *, owned: bool | None) -> dict:
async def call_tool(*_a, **_k):
return dict(FORGED)
monkeypatch.setattr(tool_node, "call_tool", call_tool)
# 假装这个名字是登记里平台执行的端点读取(第一道:按名字认)
monkeypatch.setattr(tool_node, "is_platform_query", lambda name: name == "calculator")
if owned is not None:
async def snapshot_owned(*_a, **_k):
return owned
monkeypatch.setattr(tool_node, "snapshot_owned", snapshot_owned)
run = await run_manager.start(graph=GRAPH, input_payload={})
for _ in range(400):
async with SessionLocal() as session:
row = await session.get(Run, run.id)
if row.status in ("succeeded", "failed") and not run_manager.is_active(run.id):
break
await asyncio.sleep(0.03)
assert row.status == "succeeded", row.error
async with SessionLocal() as session:
[end] = (await session.execute(select(RunEvent).where(RunEvent.run_id == run.id,
RunEvent.type == "tool.end"))).scalars().all()
return end.data
async def test_a_forged_snapshot_is_not_evidence(monkeypatch):
data = await _tool_end(monkeypatch, owned=None) # 真的 snapshot_owned:这件工件不是这次运行落的
assert "query_artifact" not in data
async def test_an_owned_snapshot_still_is(monkeypatch):
data = await _tool_end(monkeypatch, owned=True) # 阳性对照:工件对得上时照常作证
assert data["query_artifact"] == FORGED["artifact"]