mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-30 01:52:18 +00:00
fix(mcp): limit bare delete-verb precedence to leading or conjoined verbs
Co-Authored-By: bot_apk <apk@cognition.ai>
This commit is contained in:
parent
cb1ac02814
commit
e7456e8a0f
3 changed files with 25 additions and 8 deletions
|
|
@ -116,9 +116,19 @@ def _token_variants(token: str) -> frozenset[str]:
|
|||
return frozenset(variant for variant in variants if variant)
|
||||
|
||||
|
||||
_CONJUNCTION_TOKENS: Final = frozenset({"and", "then", "or", "n"})
|
||||
|
||||
|
||||
def _has_bare_delete_verb(tokens: tuple[str, ...]) -> bool:
|
||||
return any(
|
||||
token in _DELETE_TOKENS and (index == 0 or tokens[index - 1] in _CONJUNCTION_TOKENS)
|
||||
for index, token in enumerate(tokens)
|
||||
)
|
||||
|
||||
|
||||
def _classify_tokens(tokens: Iterable[str]) -> ToolOperation:
|
||||
token_tuple: Final = tuple(tokens)
|
||||
if frozenset(token_tuple) & _DELETE_TOKENS:
|
||||
if _has_bare_delete_verb(token_tuple):
|
||||
return "delete"
|
||||
token_set: Final = frozenset(chain.from_iterable(map(_token_variants, token_tuple)))
|
||||
if token_set & _READ_TOKENS:
|
||||
|
|
@ -136,10 +146,11 @@ def classify_tool_op(name: str, description: str | None = None) -> ToolOperation
|
|||
"""Classify a tool by exact token match on its name, falling back to the
|
||||
description's words only when the name yields no recognized token.
|
||||
|
||||
A bare destructive verb outranks read tokens, so ``get_and_delete_item``
|
||||
is delete while inflected forms only match through variants: precedence
|
||||
is then read > delete > update > create, keeping ``get_removed_entries``
|
||||
read. A misleading description cannot override a recognized name."""
|
||||
A bare destructive verb outranks read tokens only when it leads the name
|
||||
or follows a conjunction, so ``get_and_delete_item`` is delete while
|
||||
``describe_purge_job`` and ``getDeleteStatus`` are read. Inflected forms
|
||||
only match through variants under read > delete > update > create, and a
|
||||
misleading description cannot override a recognized name."""
|
||||
by_name: Final = _classify_tokens(_name_tokens(name))
|
||||
if by_name != "unknown":
|
||||
return by_name
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
{"name": "get_removed_entries", "description": null, "expected": "read"},
|
||||
{"name": "list_deleted_items", "description": null, "expected": "read"},
|
||||
{"name": "find_deleted", "description": null, "expected": "read"},
|
||||
{"name": "describe_purge_job", "description": null, "expected": "delete"},
|
||||
{"name": "describe_purge_job", "description": null, "expected": "read"},
|
||||
{"name": "updateItem", "description": null, "expected": "update"},
|
||||
{"name": "create-record", "description": null, "expected": "create"},
|
||||
{"name": "foo", "description": "Deletes the file", "expected": "delete"},
|
||||
|
|
@ -24,5 +24,6 @@
|
|||
{"name": "settings", "description": null, "expected": "unknown"},
|
||||
{"name": "get_and_delete_item", "description": null, "expected": "delete"},
|
||||
{"name": "fetchAndDelete", "description": null, "expected": "delete"},
|
||||
{"name": "read_then_remove", "description": null, "expected": "delete"}
|
||||
{"name": "read_then_remove", "description": null, "expected": "delete"},
|
||||
{"name": "getDeleteStatus", "description": null, "expected": "read"}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -98,9 +98,14 @@ const tokenVariants = (token: string): string[] =>
|
|||
(variant) => variant.length > 0,
|
||||
);
|
||||
|
||||
const CONJUNCTION_TOKENS = new Set(["and", "then", "or", "n"]);
|
||||
|
||||
// Matches litellm/proxy/_experimental/mcp_server/tool_classification.py, fixture-pinned.
|
||||
const classifyTokens = (tokens: string[]): CrudOp => {
|
||||
if (tokens.some((token) => DELETE_TOKENS.has(token))) return "delete";
|
||||
const hasBareDeleteVerb = tokens.some(
|
||||
(token, index) => DELETE_TOKENS.has(token) && (index === 0 || CONJUNCTION_TOKENS.has(tokens[index - 1])),
|
||||
);
|
||||
if (hasBareDeleteVerb) return "delete";
|
||||
const variants = new Set(tokens.flatMap((token) => tokenVariants(token)));
|
||||
if ([...variants].some((variant) => READ_TOKENS.has(variant))) return "read";
|
||||
if ([...variants].some((variant) => DELETE_TOKENS.has(variant))) return "delete";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue