muse-agent-example/tests/e2e/test_sqlite_write_path.py

150 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""P0.3 写路径:dispatch/flow 落 sqlite run+events,revise 带 diff,同输入可重跑 diff。"""
from __future__ import annotations
import json
import pathlib
import subprocess
import sys
import tempfile
import unittest
ROOT = next(
parent
for parent in (pathlib.Path(__file__).resolve().parent, *pathlib.Path(__file__).resolve().parents)
if (parent / "AGENTS.md").is_file() and (parent / ".git").exists()
)
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
DISPATCH_SCRIPTS = (
ROOT / "muse" / "lifecycle" / "dispatch" / "skills" / "派发智能体任务" / "scripts"
)
EVIDENCE_DIR = ROOT / "muse" / "authority" / "evidence" / "skills" / "记录运行证据" / "scripts"
DISPATCH_TESTS = ROOT / "tests" / "skills" / "派发智能体任务"
for path in (DISPATCH_SCRIPTS, EVIDENCE_DIR, DISPATCH_TESTS):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
from framework.adapters.pi.runner import ExecutionPolicy # noqa: E402
from muse.flow.dispatch import run_dispatch # noqa: E402
from muse.store import add_review, connect, get_run, list_events # noqa: E402
from test_dispatch_agent_task import ( # noqa: E402
FIXED_MODEL,
fake_launcher,
make_spec,
pi_stream_lines,
)
class SqliteWritePathTest(unittest.TestCase):
def setUp(self) -> None:
self.tmp = pathlib.Path(tempfile.mkdtemp())
self.db = self.tmp / "muse.db"
self.spec = make_spec(self.tmp)
def _dispatch(self, run_id: str, run_dir: pathlib.Path):
return run_dispatch(
self.spec,
repo_root=ROOT,
policy=ExecutionPolicy(provider="p", model=FIXED_MODEL),
run_id=run_id,
run_dir=run_dir,
launcher=fake_launcher(
pi_stream_lines(
'{"title":"重启","beats":["警报","分歧","决断"]}', model=FIXED_MODEL
)
),
trigger_source="diagnostic",
sqlite_path=self.db,
)
def test_write_path_run_events_revise_and_rerun_diff(self) -> None:
before = subprocess.check_output(["git", "status", "--short"], cwd=ROOT, text=True)
receipt, code = self._dispatch("p03-write-1", self.tmp / "run-1")
self.assertEqual(code, 0, receipt)
self.assertEqual(receipt["status"], "completed")
self.assertEqual((receipt.get("evidence") or {}).get("status"), "sqlite")
row = get_run("p03-write-1", self.db)
self.assertIsNotNone(row)
self.assertEqual(row["kind"], "agent.planner")
self.assertIn("重启", row["output_text"])
events = list_events("p03-write-1", self.db)
kinds = [item["kind"] for item in events]
self.assertIn("run.started", kinds)
self.assertIn("run.completed", kinds)
self.assertTrue(any(kind.startswith("model.") or kind == "agent.started" for kind in kinds))
with connect(self.db) as conn:
mode = conn.execute("PRAGMA journal_mode").fetchone()[0]
self.assertEqual(str(mode).lower(), "wal")
add_review(
run_id="p03-write-1",
target="candidate",
action="revise",
reviewer="qingse",
reason="钩子再紧一寸",
before_text=row["output_text"],
after_text='{"title":"重启","beats":["警报","摊牌","决断"]}',
path=self.db,
)
with connect(self.db) as conn:
review = conn.execute(
"SELECT * FROM reviews WHERE run_id=?", ("p03-write-1",)
).fetchone()
revision = conn.execute(
"SELECT * FROM revisions WHERE review_id=?", (review["id"],)
).fetchone()
self.assertEqual(review["action"], "revise")
self.assertNotEqual(revision["before_text"], revision["after_text"])
receipt2, code2 = self._dispatch("p03-write-2", self.tmp / "run-2")
self.assertEqual(code2, 0, receipt2)
replay = get_run("p03-write-2", self.db)
first_input = json.loads(row["input_json"])
second_input = json.loads(replay["input_json"])
self.assertEqual(first_input["specSha256"], second_input["specSha256"])
self.assertEqual(row["output_text"], replay["output_text"])
after = subprocess.check_output(["git", "status", "--short"], cwd=ROOT, text=True)
self.assertEqual(before, after)
ignored = subprocess.run(
["git", "check-ignore", "-q", "data/muse.db"],
cwd=ROOT,
)
self.assertEqual(ignored.returncode, 0)
def test_search_and_freeze_recall_share_sqlite_vectors(self) -> None:
from muse.store import upsert_card
freeze_scripts = ROOT / "muse" / "lifecycle" / "context" / "skills" / "固定任务上下文" / "scripts"
search_scripts = ROOT / "muse" / "lifecycle" / "context" / "skills" / "检索知识" / "scripts"
sys.path.insert(0, str(freeze_scripts))
sys.path.insert(0, str(search_scripts))
import recall
from search import search_cards
upsert_card(
card_id="c1",
kind="craft",
title="递进揭示",
payload={"型": "craft", "名称": "递进揭示", "一句话摘要": "逐层加压"},
embedding=[1.0, 0.0],
path=self.db,
)
cards = search_cards(
"揭示",
sqlite_path=self.db,
embedder=lambda _intent: [1.0, 0.0],
)
recalled = recall.recall_cards([1.0, 0.0], kind="craft", sqlite_path=self.db)
self.assertEqual(cards[0]["cardId"], "c1")
self.assertEqual(recalled[0]["cardId"], "c1")
self.assertAlmostEqual(cards[0]["score"], 1.0)
if __name__ == "__main__":
unittest.main()