Files
opensquilla--opensquilla/tests/test_engine/test_tool_context_runtime_events.py
2026-07-13 13:12:33 +08:00

252 lines
7.6 KiB
Python

from __future__ import annotations
import asyncio
import json
from dataclasses import replace
from typing import Any
from opensquilla.engine import Agent, AgentConfig
from opensquilla.sandbox.config import SandboxSettings
from opensquilla.sandbox.integration import configure_runtime, reset_runtime
from opensquilla.tool_boundary import ToolCall
from opensquilla.tools.dispatch import build_tool_handler
from opensquilla.tools.registry import ToolRegistry, get_default_registry
from opensquilla.tools.types import (
InteractionMode,
ToolContext,
ToolSpec,
current_tool_context,
)
class _Provider:
provider_name = "fake"
def chat(self, messages, tools=None, config=None):
raise AssertionError("provider should not be used")
async def list_models(self) -> list[Any]:
return []
def test_agent_wires_tool_context_runtime_events(tmp_path) -> None:
runtime_events_path = tmp_path / "runtime_events.jsonl"
agent = Agent(
provider=_Provider(),
config=AgentConfig(
runtime_events_path=str(runtime_events_path),
metadata={"agent_id": "configured-agent"},
),
session_key="agent-session",
tool_context=ToolContext(),
)
assert agent._tool_context is not None
assert agent._tool_context.on_runtime_event is not None
agent._tool_context.on_runtime_event(
{
"feature": "fresh_read_guard",
"name": "fresh_read_guard.blocked",
"tool": "edit_file",
"outcome": "blocked",
}
)
events = [
json.loads(line)
for line in runtime_events_path.read_text(encoding="utf-8").splitlines()
]
assert len(events) == 1
assert events[0]["feature"] == "fresh_read_guard"
assert events[0]["name"] == "fresh_read_guard.blocked"
assert events[0]["tool"] == "edit_file"
assert events[0]["outcome"] == "blocked"
assert events[0]["session_key"] == "agent-session"
assert events[0]["agent_id"] == "configured-agent"
assert "created_at" in events[0]
assert "timestamp" in events[0]
def test_agent_preserves_tool_context_runtime_event_identity(tmp_path) -> None:
runtime_events_path = tmp_path / "runtime_events.jsonl"
agent = Agent(
provider=_Provider(),
config=AgentConfig(
runtime_events_path=str(runtime_events_path),
metadata={"agent_id": "configured-agent"},
),
session_key="agent-session",
tool_context=ToolContext(),
)
assert agent._tool_context is not None
assert agent._tool_context.on_runtime_event is not None
agent._tool_context.on_runtime_event(
{
"feature": "fresh_read_guard",
"name": "fresh_read_guard.blocked",
"agent_id": "tool-agent",
"session_key": "tool-session",
"outcome": "blocked",
}
)
event = json.loads(runtime_events_path.read_text(encoding="utf-8"))
assert event["agent_id"] == "tool-agent"
assert event["session_key"] == "tool-session"
def test_agent_backfills_none_tool_context_runtime_event_identity(tmp_path) -> None:
runtime_events_path = tmp_path / "runtime_events.jsonl"
agent = Agent(
provider=_Provider(),
config=AgentConfig(
runtime_events_path=str(runtime_events_path),
metadata={"agent_id": "configured-agent"},
),
session_key="agent-session",
tool_context=ToolContext(),
)
assert agent._tool_context is not None
assert agent._tool_context.on_runtime_event is not None
agent._tool_context.on_runtime_event(
{
"feature": "fresh_read_guard",
"name": "fresh_read_guard.blocked",
"agent_id": None,
"session_key": None,
"outcome": "blocked",
}
)
event = json.loads(runtime_events_path.read_text(encoding="utf-8"))
assert event["agent_id"] == "configured-agent"
assert event["session_key"] == "agent-session"
def test_agent_runtime_event_callback_reaches_prebuilt_tool_handler(tmp_path) -> None:
runtime_events_path = tmp_path / "runtime_events.jsonl"
ctx = ToolContext()
registry = ToolRegistry()
async def emit_event() -> str:
active = current_tool_context.get()
assert active is ctx
assert active.on_runtime_event is not None
active.on_runtime_event(
{
"feature": "tool_runtime",
"name": "tool_runtime.probe",
"outcome": "used",
}
)
return "ok"
registry.register(
ToolSpec(
name="emit_event",
description="Emit a runtime event.",
parameters={},
required=[],
),
emit_event,
)
handler = build_tool_handler(registry, ctx)
agent = Agent(
provider=_Provider(),
config=AgentConfig(
runtime_events_path=str(runtime_events_path),
metadata={"agent_id": "configured-agent"},
),
tool_handler=handler,
session_key="agent-session",
tool_context=ctx,
)
assert agent._tool_context is ctx
assert ctx.on_runtime_event is not None
import asyncio
result = asyncio.run(handler(ToolCall("call-1", "emit_event", {})))
assert result.content == "ok"
event = json.loads(runtime_events_path.read_text(encoding="utf-8"))
assert event["feature"] == "tool_runtime"
assert event["name"] == "tool_runtime.probe"
assert event["agent_id"] == "configured-agent"
assert event["session_key"] == "agent-session"
def test_agent_runtime_event_context_reaches_replaced_builtin_tool_handler(
tmp_path,
) -> None:
runtime_events_path = tmp_path / "runtime_events.jsonl"
target = tmp_path / "src" / "app.py"
target.parent.mkdir()
target.write_text(
"def run():\n"
" if enabled:\n"
" return 1\n",
encoding="utf-8",
)
configure_runtime(
SandboxSettings(
sandbox=False,
security_grading=False,
allow_legacy_mode=True,
),
workspace=tmp_path,
)
try:
handler_ctx = ToolContext(
is_owner=True,
interaction_mode=InteractionMode.UNATTENDED,
workspace_dir=str(tmp_path),
elevated="bypass",
)
handler = build_tool_handler(get_default_registry(), handler_ctx)
agent_ctx = replace(handler_ctx, session_key="agent-session")
agent = Agent(
provider=_Provider(),
config=AgentConfig(
runtime_events_path=str(runtime_events_path),
metadata={"agent_id": "configured-agent"},
),
tool_handler=handler,
session_key="agent-session",
tool_context=agent_ctx,
)
assert agent.tool_handler is not None
result = asyncio.run(
agent.tool_handler(
ToolCall(
"call-1",
"edit_file",
{
"path": str(target),
"old_text": "if enabled:\n return 1\n",
"new_text": "if enabled:\n return 2\n",
},
)
)
)
finally:
reset_runtime()
assert result.is_error is False
events = [
json.loads(line)
for line in runtime_events_path.read_text(encoding="utf-8").splitlines()
]
assert any(event["name"] == "edit_file.flexible_match_used" for event in events)
assert events[0]["agent_id"] == "main"
assert events[0]["session_key"] == "agent-session"