feat(skill): namespace inner tools as {skill}__{tool} and detect co…#29
Open
berges99 wants to merge 1 commit into
Open
feat(skill): namespace inner tools as {skill}__{tool} and detect co…#29berges99 wants to merge 1 commit into
{skill}__{tool} and detect co…#29berges99 wants to merge 1 commit into
Conversation
…llisions eagerly
Default-on auto-namespacing for skill-internal tools, plus an eager
construction-time check that catches every static tool-name collision the
LLM could ever see. Two motivations:
1. Weaker LLMs frequently confuse a skill name with a tool name and try
to call the skill directly. Prefixing inner tools makes provenance
explicit in the function list and steers the model toward the right
call. The system prompt is updated to teach the convention when any
namespaced skill is present.
2. Inner tools could silently shadow top-level tools — `_register` only
warned and dropped the duplicate. Namespacing eliminates the most
common collision class structurally, and the eager check fails fast
on anything that slips through (including two skills slugifying to
the same prefix, e.g. `my.skill` vs `my_skill`).
Implementation notes:
- Skill names are slugified to `[a-zA-Z0-9_-]`, runs of `_` collapsed.
Combined `{prefix}__{tool}` is capped at 64 chars (OpenAI/Anthropic
limit) and validated up front.
- `namespace_tools=False` keeps the legacy flat names for callers who
rely on the old behaviour or have already designed around it.
- Renaming is idempotent across `Skill()` constructions via a class-level
`id(tool) -> base_name` map. Required because `sys.modules` shares the
imported Tool object across constructions; without it a second
`Skill(path=...)` would compound the prefix to `cars__cars__search_cars`.
- The full cached_property chain on Tool is invalidated whenever a tool
is renamed (params_model, params_model_schema, _formatted_params_schema,
anthropic_schema, openai_chat_completions_schema, openai_responses_schema).
Normal agent construction never populates these before the rename, but
toggling `namespace_tools` between two Skills for the same path was
serving stale per-provider schemas — fixed and pinned by a regression
test.
- `Agent.model_post_init` enumerates every static-known LLM-visible
name (top-level tools, skill-internal tools, `read_skill`) and raises
`ValueError` on duplicates, naming both origins. Dynamic ToolSets
whose `resolve()` runs at runtime are deferred to a defensive
`logger.error` in `_resolve_tools._register` (demoted from warning;
should be unreachable for static configs).
Tests cover both with- and without-namespace flows end-to-end:
- All three provider schema serializers asserted under both modes.
- Cache-invalidation regression: warm every cached schema on Skill #1,
reconstruct as flat, assert every schema reports the bare name.
- Idempotency across re-construction (no `cars__cars__` compounding).
- Slugification, empty-prefix and >64-char name validation.
- 5 collision scenarios in `TestAgentEagerCollisionDetection`
(flat-skill vs top-level, two flat skills, two namespaced skills not
colliding, two skills slugifying to same prefix, error message names
both origins).
- Codegen + integration trace tests updated to expect the prefixed
paths (`support_agent.payment_processing__process_refund`, etc.).
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.
…llisions eagerly
Default-on auto-namespacing for skill-internal tools, plus an eager construction-time check that catches every static tool-name collision the LLM could ever see. Two motivations:
_registeronly warned and dropped the duplicate. Namespacing eliminates the most common collision class structurally, and the eager check fails fast on anything that slips through (including two skills slugifying to the same prefix, e.g.my.skillvsmy_skill).Implementation notes:
[a-zA-Z0-9_-], runs of_collapsed. Combined{prefix}__{tool}is capped at 64 chars (OpenAI/Anthropic limit) and validated up front.namespace_tools=Falsekeeps the legacy flat names for callers who rely on the old behaviour or have already designed around it.Skill()constructions via a class-levelid(tool) -> base_namemap. Required becausesys.modulesshares the imported Tool object across constructions; without it a secondSkill(path=...)would compound the prefix tocars__cars__search_cars.namespace_toolsbetween two Skills for the same path was serving stale per-provider schemas — fixed and pinned by a regression test.Agent.model_post_initenumerates every static-known LLM-visible name (top-level tools, skill-internal tools,read_skill) and raisesValueErroron duplicates, naming both origins. Dynamic ToolSets whoseresolve()runs at runtime are deferred to a defensivelogger.errorin_resolve_tools._register(demoted from warning; should be unreachable for static configs).Tests cover both with- and without-namespace flows end-to-end:
cars__cars__compounding).TestAgentEagerCollisionDetection(flat-skill vs top-level, two flat skills, two namespaced skills not colliding, two skills slugifying to same prefix, error message names both origins).support_agent.payment_processing__process_refund, etc.).Note
Medium Risk
Changes the public tool names exposed to the LLM (and thus user prompts, traces, and provider schemas) and adds new construction-time validation that can raise
ValueErrorfor previously-accepted configurations. The behavior is well-covered by tests but could be a breaking change for callers relying on flat skill tool names.Overview
Skill-internal tools are now namespaced by default as
{skill}__{tool}, including slugification/length validation to meet provider tool-name constraints and cache invalidation so provider schemas reflect the renamed tool.Agent initialization now fails fast on any statically-detectable tool-name collision across top-level tools, skill-internal tools (including when
namespace_tools=False), andread_skill; runtime duplicate registration is treated as an error log and dropped as a last-resort fallback.Tests are updated/expanded to assert the new names in tool lists, provider schemas, and trace paths (standalone and workflow-wrapped), and to cover opt-out behavior and multiple collision/edge cases.
Reviewed by Cursor Bugbot for commit a722deb. Bugbot is set up for automated code reviews on this repo. Configure here.