mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 01:50:44 +08:00
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
302 lines
11 KiB
Python
302 lines
11 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import types
|
|
from pathlib import Path
|
|
|
|
|
|
def _load_handler_module():
|
|
p = Path("d:/project/chatgpt/oclaw/runtime/hooks/bundled/wiki-auto-inject/handler.py").resolve()
|
|
spec = importlib.util.spec_from_file_location("wiki_auto_inject_handler_test", str(p))
|
|
assert spec and spec.loader
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod) # type: ignore[assignment]
|
|
return mod
|
|
|
|
|
|
def test_collect_snippets_top_k_and_relevance(tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "a.md").write_text(
|
|
"# A\nRouter VLAN troubleshooting guide\nA guide for vlan and trunk setup.\n",
|
|
encoding="utf-8",
|
|
)
|
|
(wiki / "b.md").write_text(
|
|
"# B\nUnrelated note.\nAnother line.\n",
|
|
encoding="utf-8",
|
|
)
|
|
(wiki / "c.md").write_text(
|
|
"# C\nVLAN tagging tips for router.\nrouter vlan quick checklist.\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
text, meta = mod._collect_snippets(wiki, "router vlan", max_chars=500, top_k=2)
|
|
lines = [x for x in text.splitlines() if x.strip()]
|
|
assert len(lines) == 2
|
|
assert len(meta) == 2
|
|
assert "a.md" in text or "c.md" in text
|
|
assert "b.md" not in text
|
|
|
|
|
|
def test_handle_sets_prepend_system_context(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("Router vlan baseline\n", encoding="utf-8")
|
|
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {"enabled": True, "inject": {"max_chars": 800, "top_k": 3}},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "router vlan", "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
out = str(event.context.get("prepend_system_context") or "")
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert "Wiki Context (Auto Inject)" in out
|
|
assert "ops.md" in out
|
|
assert bool(meta.get("enabled")) is True
|
|
assert isinstance(meta.get("hits"), list)
|
|
|
|
|
|
def test_handle_ultra_saver_skips_short_query(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("router vlan baseline\n", encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {
|
|
"enabled": True,
|
|
"inject": {
|
|
"max_chars": 800,
|
|
"top_k": 3,
|
|
"ultra_saver_enabled": True,
|
|
"min_query_chars": 20,
|
|
"require_topic_hint": True,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "hi", "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
assert str(event.context.get("prepend_system_context") or "") == ""
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert bool(meta.get("enabled")) is False
|
|
assert str(meta.get("skip_reason") or "") == "short_query"
|
|
|
|
|
|
def test_handle_ultra_saver_requires_topic_hint(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("router vlan baseline\n", encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {
|
|
"enabled": True,
|
|
"inject": {
|
|
"max_chars": 800,
|
|
"top_k": 3,
|
|
"ultra_saver_enabled": True,
|
|
"min_query_chars": 5,
|
|
"require_topic_hint": True,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "summarize this message", "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
assert str(event.context.get("prepend_system_context") or "") == ""
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert bool(meta.get("enabled")) is False
|
|
assert str(meta.get("skip_reason") or "") == "no_topic_hint"
|
|
|
|
|
|
def test_handle_store_only_mode_skips_injection(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("router vlan baseline\n", encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {"enabled": True, "inject": {"max_chars": 800, "top_k": 3}},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "router vlan", "memory_mode": "store_only", "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
assert str(event.context.get("prepend_system_context") or "") == ""
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert bool(meta.get("enabled")) is False
|
|
assert str(meta.get("skip_reason") or "") == "memory_mode_store_only"
|
|
|
|
|
|
def test_handle_respects_manager_gate_off(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("router vlan baseline\n", encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {"enabled": True, "inject": {"max_chars": 800, "top_k": 3}},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "router vlan", "need_wiki_inject": False, "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
assert str(event.context.get("prepend_system_context") or "") == ""
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert bool(meta.get("enabled")) is False
|
|
assert str(meta.get("skip_reason") or "") == "manager_gate_off"
|
|
|
|
|
|
def test_handle_requires_wiki_query_when_manager_gate_on(monkeypatch, tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
wiki.mkdir(parents=True, exist_ok=True)
|
|
(wiki / "ops.md").write_text("router vlan baseline\n", encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
mod,
|
|
"_load_config",
|
|
lambda: {
|
|
"plugins": {
|
|
"entries": {
|
|
"memory-wiki": {
|
|
"wiki_root": str(wiki),
|
|
"auto": {"enabled": True, "inject": {"max_chars": 800, "top_k": 3}},
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
event = types.SimpleNamespace(
|
|
type="llm",
|
|
action="before_prompt_build",
|
|
context={"userText": "router vlan", "need_wiki_inject": True, "prepend_system_context": ""},
|
|
)
|
|
mod.handle(event)
|
|
assert str(event.context.get("prepend_system_context") or "") == ""
|
|
meta = event.context.get("wiki_inject_meta") or {}
|
|
assert bool(meta.get("enabled")) is False
|
|
assert str(meta.get("skip_reason") or "") == "manager_wiki_query_missing"
|
|
|
|
|
|
def test_collect_snippets_prioritizes_merged_turns(tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
(wiki / "inbox").mkdir(parents=True, exist_ok=True)
|
|
(wiki / ".oclaw").mkdir(parents=True, exist_ok=True)
|
|
(wiki / "inbox" / "merged-turns.md").write_text("router vlan merged signal\n", encoding="utf-8")
|
|
(wiki / "normal.md").write_text("router vlan normal note\n", encoding="utf-8")
|
|
(wiki / ".oclaw" / "index.json").write_text(
|
|
'{"files":["normal.md","inbox/merged-turns.md"]}',
|
|
encoding="utf-8",
|
|
)
|
|
text, meta = mod._collect_snippets(wiki, "router vlan", max_chars=800, top_k=1)
|
|
assert "inbox/merged-turns.md" in text
|
|
assert meta and str(meta[0].get("source") or "").endswith("merged-turns.md")
|
|
|
|
|
|
def test_collect_snippets_prefers_topic_page_before_merged(tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
(wiki / "inbox").mkdir(parents=True, exist_ok=True)
|
|
(wiki / "topics").mkdir(parents=True, exist_ok=True)
|
|
(wiki / ".oclaw").mkdir(parents=True, exist_ok=True)
|
|
(wiki / "topics" / "auto-network.md").write_text("router vlan topic page\n", encoding="utf-8")
|
|
(wiki / "inbox" / "merged-turns.md").write_text("router vlan merged page\n", encoding="utf-8")
|
|
(wiki / ".oclaw" / "topic-index.json").write_text(
|
|
'{"topics":{"network":{"path":"topics/auto-network.md","merged_count":3}}}',
|
|
encoding="utf-8",
|
|
)
|
|
text, meta = mod._collect_snippets(wiki, "router vlan", max_chars=900, top_k=1)
|
|
assert "topics/auto-network.md" in text
|
|
assert meta and str(meta[0].get("source") or "").endswith("topics/auto-network.md")
|
|
|
|
|
|
def test_collect_snippets_honors_configured_topic_rules(tmp_path: Path) -> None:
|
|
mod = _load_handler_module()
|
|
wiki = tmp_path / "wiki"
|
|
(wiki / "topics").mkdir(parents=True, exist_ok=True)
|
|
(wiki / ".oclaw").mkdir(parents=True, exist_ok=True)
|
|
(wiki / "topics" / "auto-iac.md").write_text("terraform module baseline\n", encoding="utf-8")
|
|
(wiki / ".oclaw" / "topic-index.json").write_text(
|
|
'{"topics":{"iac":{"path":"topics/auto-iac.md","merged_count":1}}}',
|
|
encoding="utf-8",
|
|
)
|
|
rules = [{"topic": "iac", "keywords": ["terraform", "ansible"]}]
|
|
text, meta = mod._collect_snippets(
|
|
wiki,
|
|
"terraform drift",
|
|
max_chars=800,
|
|
top_k=1,
|
|
topic_rules=rules,
|
|
)
|
|
assert "topics/auto-iac.md" in text
|
|
assert meta and str(meta[0].get("source") or "").endswith("topics/auto-iac.md")
|