feat(pydantic_ai): add PYD-013, ambiguous tool name - #107
Conversation
Ports the ambiguous-name check to pydantic_ai. Claude SDK (CSDK-007), OpenAI (OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the five newer packs had none. The tool name sits directly beside the description in what the model sees, so it is half the selection signal, and a generic verb like process or handle spends that half on nothing.
|
Thanks @bradAGI, this is a clean port. Checking One question before this goes in. Discovery honors an explicit Small nit, the tail of the |
Ports the ambiguous-name check to Pydantic AI. Claude SDK (CSDK-007), OpenAI (OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the newer packs had none.
The tool name sits directly beside the description in what the model sees, so it is half the selection signal — and a generic verb like
processorhandlespends that half on nothing. Pydantic AI derives the tool name from the function name unless one is passed explicitly, so a generically named helper function silently becomes a generically named tool — the defect arrives without anyone deciding to name a toolprocess.Same
name_inlist as CSDK-007, so behavior stays consistent across packs.Verification — engine built at
main:Fire (tool named
process): includesPYD-013Silent (same tool renamed
summarize_invoice):PYD-013absentA note on scope. I drafted a fifth rule in this batch for the Vercel AI SDK and dropped it after testing:
ToolDef.Nameis deliberately empty forvercel_ai_tool, because the SDK derives a tool's name from the key in thetools: { ... }record rather than from thetool({...})call. The engine asserts this directly —so every name-based predicate (
name_in,name_has_prefix) is structurally inert for that pack — it also blocks a Vercel idempotency rule, which is why the pack has none. Back-fillingNamefrom the record key at discovery time would unblock both. Happy to open that engine PR if it'd be useful.Paired engine PR follows, mirroring the rule into
testdata/rules-fixture/with fire/silent cases.