feat(agent): expose request routing metadata through my
This commit is contained in:
@@ -77,8 +77,11 @@ class MyTool(Tool):
|
||||
"exec_config", # inspect allowed (e.g. check sandbox), modify blocked
|
||||
"web_config", # inspect allowed (e.g. check enable), modify blocked
|
||||
"workspace_sandbox", # read-only view of workspace enforcement level
|
||||
"request", # current message routing metadata
|
||||
})
|
||||
|
||||
_REQUEST_FIELDS = ("channel", "chat_id", "sender_id")
|
||||
|
||||
_DENIED_ATTRS = frozenset({
|
||||
"__class__", "__dict__", "__bases__", "__subclasses__", "__mro__",
|
||||
"__init__", "__new__", "__reduce__", "__getstate__", "__setstate__",
|
||||
@@ -141,6 +144,8 @@ class MyTool(Tool):
|
||||
"Scratchpad keys persist across turns but not restarts.\n"
|
||||
"Key values: _current_iteration (current progress), "
|
||||
"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"
|
||||
"\n"
|
||||
"When to use:\n"
|
||||
@@ -173,6 +178,7 @@ class MyTool(Tool):
|
||||
"key": {
|
||||
"type": "string",
|
||||
"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.",
|
||||
},
|
||||
"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:
|
||||
if not key:
|
||||
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:
|
||||
found, value = self._current_runtime_value(key)
|
||||
if found:
|
||||
|
||||
@@ -1133,6 +1133,42 @@ class TestLastUsageInSummary:
|
||||
|
||||
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):
|
||||
tool = _make_tool()
|
||||
ctx = RequestContext(
|
||||
|
||||
Reference in New Issue
Block a user