mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 05:20:44 +08:00
feat: integrate netx ops data access and unify prompt/workspace runtime
Migrate prompt templates into runtime workspaces, add network_ops netx toolchain (latest batch injection, raw fields, SQL query), and align docs/tests/admin/runtime wiring so ops analysis can query netx detail data through stable role-scoped paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
83f46f8aa2
commit
b0e0c05d8a
60 changed files with 1143 additions and 46 deletions
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from oclaw.prompts.frontmatter import parse_frontmatter_dict, parse_markdown_document
|
||||
from oclaw.runtime.prompt_templates.frontmatter import parse_frontmatter_dict, parse_markdown_document
|
||||
|
||||
|
||||
def test_parse_yaml_frontmatter_nested_metadata() -> None:
|
||||
|
|
|
|||
85
tests/test_ops_netx_system_inject.py
Normal file
85
tests/test_ops_netx_system_inject.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from oclaw.platform.llm.chat_models import RuleBasedChatModel
|
||||
from oclaw.platform.persistence.sqlite_store import SqliteStore
|
||||
from oclaw.runtime.direct_loop import _build_model_context
|
||||
|
||||
|
||||
def test_ops_role_injects_netx_batch_anchor_into_system(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
import oclaw.runtime.tools.experts.network_ops.netx_tools as nt
|
||||
|
||||
def _fake_latest() -> dict:
|
||||
return {
|
||||
"ok": True,
|
||||
"batch_id": "batch-inject-test",
|
||||
"batch_row": {
|
||||
"created_at": "2026-05-01T00:00:00",
|
||||
"source_file": "alarms.xlsx",
|
||||
"status": "ok",
|
||||
"total_rows": 42,
|
||||
},
|
||||
}
|
||||
|
||||
monkeypatch.setattr(nt, "_resolve_latest_import_batch_id", _fake_latest)
|
||||
nt._OPS_NETX_SYS_CTX_CACHE.clear() # type: ignore[attr-defined]
|
||||
|
||||
store = SqliteStore(str(tmp_path / "n.sqlite"))
|
||||
sess = store.create_session("s")
|
||||
store.add_message(session_id=sess.id, role="user", content="hello")
|
||||
|
||||
msgs = _build_model_context(
|
||||
store=store,
|
||||
session_id=sess.id,
|
||||
max_messages=20,
|
||||
system_prompt="BASE",
|
||||
model=RuleBasedChatModel(),
|
||||
lang="zh",
|
||||
memory_context=None,
|
||||
trace_id=None,
|
||||
parent_span_id=None,
|
||||
tools=None,
|
||||
base_url="",
|
||||
skill_binding_role="ops",
|
||||
user_text="hello",
|
||||
)
|
||||
assert msgs and msgs[0].get("role") == "system"
|
||||
sys_text = str(msgs[0].get("content") or "")
|
||||
assert "batch-inject-test" in sys_text
|
||||
assert "alarms.xlsx" in sys_text
|
||||
|
||||
|
||||
def test_non_ops_role_skips_netx_inject(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
calls: dict[str, int] = {"n": 0}
|
||||
|
||||
def _spy_latest() -> dict:
|
||||
calls["n"] += 1
|
||||
return {"ok": True, "batch_id": "x", "batch_row": {}}
|
||||
|
||||
import oclaw.runtime.tools.experts.network_ops.netx_tools as nt
|
||||
|
||||
monkeypatch.setattr(nt, "_resolve_latest_import_batch_id", _spy_latest)
|
||||
|
||||
store = SqliteStore(str(tmp_path / "g.sqlite"))
|
||||
sess = store.create_session("s")
|
||||
store.add_message(session_id=sess.id, role="user", content="hi")
|
||||
|
||||
_build_model_context(
|
||||
store=store,
|
||||
session_id=sess.id,
|
||||
max_messages=20,
|
||||
system_prompt="BASE_ONLY",
|
||||
model=RuleBasedChatModel(),
|
||||
lang="zh",
|
||||
memory_context=None,
|
||||
trace_id=None,
|
||||
parent_span_id=None,
|
||||
tools=None,
|
||||
base_url="",
|
||||
skill_binding_role="generalist",
|
||||
user_text="hi",
|
||||
)
|
||||
assert calls["n"] == 0
|
||||
|
|
@ -7,7 +7,6 @@ from oclaw.platform.config.paths import PROJECT_ROOT
|
|||
|
||||
_PROMPT_CRITICAL_FILES = (
|
||||
"runtime/chat/agent.py",
|
||||
"runtime/agents/network_ops_agent.py",
|
||||
"runtime/agents/factory.py",
|
||||
"runtime/chat/agent_messages.py",
|
||||
"runtime/system_prompt.py",
|
||||
|
|
@ -27,16 +26,17 @@ def test_prompt_critical_paths_use_prompt_templates() -> None:
|
|||
or "render_prompt_for_lang(" in text
|
||||
or "render_runtime_prompt(" in text
|
||||
), (
|
||||
f"{rel} must render prompts from oclaw/prompts*.md"
|
||||
f"{rel} must use oclaw.runtime.prompt_templates (Markdown under runtime/workspaces/_system)"
|
||||
)
|
||||
|
||||
|
||||
def test_prompt_markdown_frontmatter_keys_present() -> None:
|
||||
roots = [
|
||||
PROJECT_ROOT / "prompts",
|
||||
PROJECT_ROOT / "prompts_runtime",
|
||||
PROJECT_ROOT / "runtime" / "workspaces" / "_system",
|
||||
]
|
||||
for prompts_root in roots:
|
||||
if not prompts_root.exists():
|
||||
continue
|
||||
for p in prompts_root.rglob("*.md"):
|
||||
if p.name == "MAPPING.md":
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from oclaw.prompts.loader import load_runtime_prompt_doc, load_prompt_doc, render_runtime_prompt, render_prompt
|
||||
from oclaw.runtime.prompt_templates import (
|
||||
load_prompt_doc,
|
||||
load_runtime_prompt_doc,
|
||||
render_prompt,
|
||||
render_runtime_prompt,
|
||||
)
|
||||
|
||||
|
||||
def test_load_prompt_doc_with_frontmatter() -> None:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from oclaw.runtime.memory_stage import render_memory_context_block
|
||||
from oclaw.runtime.types import OclawMemoryContext
|
||||
from oclaw.prompts.loader import render_runtime_prompt
|
||||
from oclaw.runtime.prompt_templates import render_runtime_prompt
|
||||
|
||||
|
||||
def test_router_prompt_renders() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue