oclaw/tests/test_oclaw_system_prompt.py
oliver 87ff00aa06 将 workspace 缓存失效机制切换到 revision token,并补齐回归验证。
通过 revision 驱动 prompt/prebuild 缓存重建,避免请求路径上的目录签名扫描成本,同时新增缓存失效与路径约定相关测试,确保行为稳定。

Made-with: Cursor
2026-04-27 15:24:39 +08:00

189 lines
5.5 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.

from __future__ import annotations
from pathlib import Path
import pytest
from oclaw.runtime.system_prompt import build_oclaw_executor_system_prompt
from oclaw.runtime.types import OclawMemoryContext
from oclaw.platform.persistence.sqlite_store import SqliteStore
from oclaw.runtime.tools.base import ToolRegistry, ToolSpec
def _spec(name: str) -> ToolSpec:
return ToolSpec(
name=name,
description="d",
parameters={"type": "object", "properties": {}},
handler=lambda _a: {"ok": True},
read_only=True,
)
def test_build_oclaw_executor_system_prompt_includes_skills_block(tmp_path: Path) -> None:
store = SqliteStore(str(tmp_path / "sp.sqlite"))
store.create_session("s")
reg = ToolRegistry([_spec("demo_tool")])
out = build_oclaw_executor_system_prompt(
store=store,
tools=reg,
base_url="",
base_system="You are helpful.",
memory_context=OclawMemoryContext(),
lang="zh",
)
assert "## 技能(skills):" in out
assert '- name:"' in out
def test_build_oclaw_executor_system_prompt_without_tools_has_no_skills_block(tmp_path: Path) -> None:
store = SqliteStore(str(tmp_path / "sp1.sqlite"))
store.create_session("s")
out = build_oclaw_executor_system_prompt(
store=store,
tools=None,
base_url="",
base_system=" plain ",
memory_context=OclawMemoryContext(),
lang="zh",
)
assert "## 技能(skills):" not in out
assert "不会自动执行技能 `scripts/` 目录下的文件" in out
assert "scripts/" in out
def test_build_oclaw_executor_system_prompt_with_tools_has_skills(tmp_path: Path) -> None:
store = SqliteStore(str(tmp_path / "sp2.sqlite"))
store.create_session("s")
reg = ToolRegistry([_spec("x")])
out = build_oclaw_executor_system_prompt(
store=store,
tools=reg,
base_url="",
base_system="base",
memory_context=OclawMemoryContext(),
lang="zh",
)
assert "## 技能(skills):" in out
assert '- name:"' in out
def test_executor_static_prompt_cache_invalidates_on_settings_change(monkeypatch: pytest.MonkeyPatch) -> None:
from oclaw.runtime import system_prompt as sp
class DummyStore:
def __init__(self) -> None:
self.values: dict[str, str] = {}
def get_setting(self, key: str) -> str:
return str(self.values.get(key, ""))
class DummyReg:
pass
calls = {"n": 0}
def _skills_block(**kwargs) -> str:
_ = kwargs
calls["n"] += 1
return "skills-block"
monkeypatch.setattr(sp, "expert_workspace_signature_token", lambda: ("sig",))
monkeypatch.setattr(sp, "build_project_context_block", lambda **kwargs: "")
monkeypatch.setattr(sp, "build_skills_catalog_block", _skills_block)
monkeypatch.setattr(
sp,
"render_runtime_prompt",
lambda prompt_id, variables, strict: f"{prompt_id}\n{variables.get('skills_catalog') or ''}",
)
store = DummyStore()
reg = DummyReg()
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
assert calls["n"] == 1
store.values["AIA_SKILL_DISABLED_NAMES"] = "[\"weather\"]"
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
assert calls["n"] == 2
def test_executor_static_prompt_cache_invalidates_on_workspace_revision_change(monkeypatch: pytest.MonkeyPatch) -> None:
from oclaw.runtime import system_prompt as sp
class DummyStore:
def get_setting(self, key: str) -> str:
_ = key
return ""
class DummyReg:
pass
calls = {"n": 0}
token = {"v": 1}
def _skills_block(**kwargs) -> str:
_ = kwargs
calls["n"] += 1
return "skills-block"
monkeypatch.setattr(sp, "expert_workspace_signature_token", lambda: ("revision", token["v"]))
monkeypatch.setattr(sp, "build_project_context_block", lambda **kwargs: "")
monkeypatch.setattr(sp, "build_skills_catalog_block", _skills_block)
monkeypatch.setattr(
sp,
"render_runtime_prompt",
lambda prompt_id, variables, strict: f"{prompt_id}\n{variables.get('skills_catalog') or ''}",
)
store = DummyStore()
reg = DummyReg()
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
assert calls["n"] == 1
token["v"] = 2
_ = sp.get_executor_prompt_static(
store=store,
tools=reg, # type: ignore[arg-type]
base_url="",
base_system="base",
workspace_dir=None,
skill_binding_role="generalist",
)
assert calls["n"] == 2