mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-04 08:28:36 +00:00
feat(agent): expose request routing metadata through my
This commit is contained in:
parent
d349b65713
commit
93de413432
@ -77,8 +77,11 @@ class MyTool(Tool):
|
|||||||
"exec_config", # inspect allowed (e.g. check sandbox), modify blocked
|
"exec_config", # inspect allowed (e.g. check sandbox), modify blocked
|
||||||
"web_config", # inspect allowed (e.g. check enable), modify blocked
|
"web_config", # inspect allowed (e.g. check enable), modify blocked
|
||||||
"workspace_sandbox", # read-only view of workspace enforcement level
|
"workspace_sandbox", # read-only view of workspace enforcement level
|
||||||
|
"request", # current message routing metadata
|
||||||
})
|
})
|
||||||
|
|
||||||
|
_REQUEST_FIELDS = ("channel", "chat_id", "sender_id")
|
||||||
|
|
||||||
_DENIED_ATTRS = frozenset({
|
_DENIED_ATTRS = frozenset({
|
||||||
"__class__", "__dict__", "__bases__", "__subclasses__", "__mro__",
|
"__class__", "__dict__", "__bases__", "__subclasses__", "__mro__",
|
||||||
"__init__", "__new__", "__reduce__", "__getstate__", "__setstate__",
|
"__init__", "__new__", "__reduce__", "__getstate__", "__setstate__",
|
||||||
@ -141,6 +144,8 @@ class MyTool(Tool):
|
|||||||
"Scratchpad keys persist across turns but not restarts.\n"
|
"Scratchpad keys persist across turns but not restarts.\n"
|
||||||
"Key values: _current_iteration (current progress), "
|
"Key values: _current_iteration (current progress), "
|
||||||
"max_iterations - _current_iteration = remaining iterations.\n"
|
"max_iterations - _current_iteration = remaining iterations.\n"
|
||||||
|
"Current routing metadata is available read-only via request.channel, "
|
||||||
|
"request.chat_id, and request.sender_id.\n"
|
||||||
"Note: web_config and exec_config are readable but read-only.\n"
|
"Note: web_config and exec_config are readable but read-only.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"When to use:\n"
|
"When to use:\n"
|
||||||
@ -173,6 +178,7 @@ class MyTool(Tool):
|
|||||||
"key": {
|
"key": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Dot-path for check/set. Examples: 'max_iterations', 'workspace', 'provider_retry_mode'. "
|
"description": "Dot-path for check/set. Examples: 'max_iterations', 'workspace', 'provider_retry_mode'. "
|
||||||
|
"Use 'request.channel', 'request.chat_id', or 'request.sender_id' for current routing metadata. "
|
||||||
"Use 'model_preset' to switch named model presets. For check without key, shows all config values.",
|
"Use 'model_preset' to switch named model presets. For check without key, shows all config values.",
|
||||||
},
|
},
|
||||||
"value": {"description": "New value (for set). Type must match target (int for max_iterations/context_window_tokens, str for model/model_preset)."},
|
"value": {"description": "New value (for set). Type must match target (int for max_iterations/context_window_tokens, str for model/model_preset)."},
|
||||||
@ -340,6 +346,19 @@ class MyTool(Tool):
|
|||||||
def _inspect(self, key: str | None) -> str:
|
def _inspect(self, key: str | None) -> str:
|
||||||
if not key:
|
if not key:
|
||||||
return self._inspect_all()
|
return self._inspect_all()
|
||||||
|
if key == "request" or key.startswith("request."):
|
||||||
|
request_ctx = current_request_context()
|
||||||
|
if request_ctx is None:
|
||||||
|
return ToolResult.error("Error: current request context is unavailable")
|
||||||
|
if key == "request":
|
||||||
|
return self._format_value(
|
||||||
|
{field: getattr(request_ctx, field) for field in self._REQUEST_FIELDS},
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
field = key.removeprefix("request.")
|
||||||
|
if field not in self._REQUEST_FIELDS:
|
||||||
|
return ToolResult.error(f"Error: '{key}' not found")
|
||||||
|
return self._format_value(getattr(request_ctx, field), key)
|
||||||
if "." not in key:
|
if "." not in key:
|
||||||
found, value = self._current_runtime_value(key)
|
found, value = self._current_runtime_value(key)
|
||||||
if found:
|
if found:
|
||||||
|
|||||||
@ -1133,6 +1133,42 @@ class TestLastUsageInSummary:
|
|||||||
|
|
||||||
class TestRequestContext:
|
class TestRequestContext:
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_check_exposes_current_routing_metadata_on_demand(self):
|
||||||
|
tool = _make_tool()
|
||||||
|
ctx = RequestContext(
|
||||||
|
channel="feishu",
|
||||||
|
chat_id="oc_abc123",
|
||||||
|
sender_id="ou_user456",
|
||||||
|
)
|
||||||
|
|
||||||
|
with request_context(ctx):
|
||||||
|
assert await tool.execute(action="check", key="request.channel") == (
|
||||||
|
"request.channel: 'feishu'"
|
||||||
|
)
|
||||||
|
assert await tool.execute(action="check", key="request.chat_id") == (
|
||||||
|
"request.chat_id: 'oc_abc123'"
|
||||||
|
)
|
||||||
|
assert await tool.execute(action="check", key="request.sender_id") == (
|
||||||
|
"request.sender_id: 'ou_user456'"
|
||||||
|
)
|
||||||
|
summary = await tool.execute(action="check")
|
||||||
|
|
||||||
|
assert "oc_abc123" not in summary
|
||||||
|
assert "ou_user456" not in summary
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_request_routing_metadata_is_read_only(self):
|
||||||
|
tool = _make_tool()
|
||||||
|
|
||||||
|
result = await tool.execute(
|
||||||
|
action="set",
|
||||||
|
key="request.chat_id",
|
||||||
|
value="replacement",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "read-only" in result
|
||||||
|
|
||||||
def test_audit_reads_bound_session(self):
|
def test_audit_reads_bound_session(self):
|
||||||
tool = _make_tool()
|
tool = _make_tool()
|
||||||
ctx = RequestContext(
|
ctx = RequestContext(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user