feat: auto-wrap tools to hide and inject static arguments#921
Open
edis-uipath wants to merge 2 commits into
Open
feat: auto-wrap tools to hide and inject static arguments#921edis-uipath wants to merge 2 commits into
edis-uipath wants to merge 2 commits into
Conversation
Add a per-tool helper that hides statically-configured tool parameters from the model-facing schema and injects the configured values just before the underlying tool runs, so the model never has to (and never can) populate them. - wrap_tool_with_static_args / wrap_tools_with_static_args (agent/tools/static_args.py): strip static params from args_schema, inject their values on call, and surface the now-hidden values in the tool description so otherwise-identical tools remain distinguishable to the model. Values flagged is_sensitive are redacted to <sensitive>. - remove_fields_from_schema (agent/tools/schema_editing.py): drop named fields from a JSON schema's properties/required by jsonpath. - Auto-wrap at the three tool-creation entry points: create_tools_from_resources (tool_factory), create_mcp_tools_and_clients (mcp_tool), and create_a2a_tools_and_clients (a2a_tool). - StaticArgsHandler in the ReAct llm_node is left untouched for back-compat. - Tests in tests/agent/tools/test_wrap_static_args.py. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds an automatic per-tool wrapper that removes statically configured tool parameters from the LLM-facing argument schema, then injects the pinned values back into the call kwargs at execution time (with sensitive static values redacted in tool descriptions). It also wires this wrapping into the main tool creation entry points and adds tests for the behavior.
Changes:
- Added
wrap_tool_with_static_args/wrap_tools_with_static_argsto hide static params fromargs_schema, inject them at invocation time, and append (redacted) static values to tool descriptions. - Added
remove_fields_from_schemahelper to drop named properties/required entries from JSON schemas by jsonpath. - Enabled auto-wrapping in
tool_factory, MCP tool creation, and A2A tool creation, with a new dedicated test suite.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
tests/agent/tools/test_wrap_static_args.py |
Adds tests covering schema hiding, call-time injection, non-mutation, passthrough behavior, schema removal helper, and sensitive redaction in descriptions. |
src/uipath_langchain/agent/tools/tool_factory.py |
Wraps the tool list returned from create_tools_from_resources with the new static-args wrapper. |
src/uipath_langchain/agent/tools/static_args.py |
Implements the new tool-wrapping logic to hide static args from schema and inject them at runtime. |
src/uipath_langchain/agent/tools/schema_editing.py |
Adds remove_fields_from_schema to remove named schema fields/properties/required by jsonpath. |
src/uipath_langchain/agent/tools/mcp/mcp_tool.py |
Applies static-args wrapping to MCP-created tools before returning them. |
src/uipath_langchain/agent/tools/a2a/a2a_tool.py |
Applies static-args wrapping to A2A-created tools before returning them. |
Comment on lines
+315
to
+323
| removed: set[str] = set() | ||
| for json_path in json_paths: | ||
| try: | ||
| segments = parse_jsonpath_segments(json_path) | ||
| except Exception: | ||
| # A malformed path is simply not removable; leave the field in place. | ||
| continue | ||
| if not segments or segments[-1] == "*": | ||
| continue |
Comment on lines
+443
to
+459
| coroutine = tool.coroutine | ||
| if coroutine is not None: | ||
| _coroutine = coroutine | ||
|
|
||
| async def _acall(**kwargs: Any) -> Any: | ||
| return await _coroutine(**apply_static_args(inject_values, kwargs)) | ||
|
|
||
| update["coroutine"] = _acall | ||
|
|
||
| func = tool.func | ||
| if func is not None: | ||
| _func = func | ||
|
|
||
| def _call(**kwargs: Any) -> Any: | ||
| return _func(**apply_static_args(inject_values, kwargs)) | ||
|
|
||
| update["func"] = _call |
Add tests/agent/tools/test_wrap_static_args_tool_types.py covering the parameter shapes a configured tool can take — static-only, static+input, all-static (no model input), no-params, no-static, large descriptions, many params, plus sensitive-value redaction and large-value truncation — crossed with the production tool kinds: MCP (raw dict schema), agent-resource (pydantic schema, with and without an execution wrapper), A2A, the IXP wrapper-only class, and a plain LangChain StructuredTool. Also verifies metadata and graph-execution wrappers survive model_copy, the original tool is never mutated, and a heterogeneous list wraps the static tools while passing the rest through unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What
Adds a per-tool wrapper that hides statically-configured tool parameters from the model-facing schema and injects their configured values just before the tool runs. The model never has to — and never can — populate a parameter the agent author already pinned.
This is a per-tool, agent-state-independent counterpart to the existing
StaticArgsHandler. Tools without staticargument_propertiespass through unchanged, and the original tool is never mutated.Key pieces
wrap_tool_with_static_args/wrap_tools_with_static_args(agent/tools/static_args.py)args_schema(so the LLM no longer sees them).coroutine/funcruns.descriptionso otherwise-identical tools stay distinguishable to the model (e.g. a send-email tool configured for Bob vs one for Alice — no reliance on tool naming).is_sensitivevalues to<sensitive>in the description — secrets are injected at execution but never exposed to the model or logs.remove_fields_from_schema(agent/tools/schema_editing.py): drops named fields from a JSON schema'sproperties/requiredby jsonpath (skips array elements and absent fields).create_tools_from_resources(tool_factory),create_mcp_tools_and_clients(mcp_tool), andcreate_a2a_tools_and_clients(a2a_tool).Why
Static parameters (e.g. a fixed Integration Service search
provider, or a configured recipient) are agent-author configuration, not model input. Previously, surfacing them in the call schema invited the model to (re)populate or override them. Hiding them removes that failure mode — but naively hiding them also erases the only signal distinguishing two tools that differ solely by a configured value. This PR resolves both: hide from the schema, inject at call time, and echo the (non-sensitive) values into the description so selection still works.StaticArgsHandlerin the ReActllm_nodeis intentionally left in place for backward compatibility; the wrapper drops only the staticargument_propertiesit bakes in, leaving non-static (argument/textBuilder/arrayBuilder) variants for the handler.Tests
tests/agent/tools/test_wrap_static_args.pycovers: static param hidden from schema + injected on call, non-static params preserved, original tool not mutated, passthrough when no static args, list mapping,remove_fields_from_schemabehavior, description lists the static name+value, sensitive value redacted (literal value absent), and same-named tools getting distinct descriptions.Verification:
ruff format --check+ruff checkclean,mypyclean,pytest tests/agent/tools/→ 636 passed.🤖 Generated with Claude Code