Skip to content

fix(patch-tool): advertise per-mode required params in schema descriptions#22143

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-8be3aa88
May 8, 2026
Merged

fix(patch-tool): advertise per-mode required params in schema descriptions#22143
teknium1 merged 2 commits into
mainfrom
hermes/hermes-8be3aa88

Conversation

@teknium1

@teknium1 teknium1 commented May 8, 2026

Copy link
Copy Markdown
Contributor

Salvages #15673 (@briandevans, 1198 commits stale) onto current main.

Summary

Strict tool callers (kimi-k2.x, and per a Slack report from Ben Eng today, llm-wiki batch edits) see only mode in the patch tool's JSON-schema required, treat path/old_string/new_string as optional, and emit incomplete patch calls — causing loops of "old_string and new_string required" and bulk batches that silently drop edits while the model claims success.

Fix is description-only: required: ["mode"] stays (Anthropic / Fireworks / Moonshot-Kimi sanitizers all reject anyOf/oneOf at the parameters level). Per-mode requirements are now called out in both the top-level tool description and each conditionally-required property's description.

Changes

  • tools/file_tools.py: PATCH_SCHEMA description restructured with REPLACE MODE / PATCH MODE sections listing REQUIRED PARAMETERS; per-property descriptions prefixed with REQUIRED when mode='replace' / REQUIRED when mode='patch'.
  • tests/tools/test_file_tools.py: two invariant tests (one for description layers, one pinning required == ["mode"] with no anyOf/oneOf to prevent re-introducing the bug). Trimmed from the original 9-test block.

Validation

scripts/run_tests.sh tests/tools/test_file_tools.py → 29 passed.

Closes #15524. Credit: @briandevans.

briandevans and others added 2 commits May 8, 2026 16:58
…tions

Models that enforce required-only constraints (e.g. kimi-k2.x) were
omitting old_string/new_string for replace mode and patch for patch mode
because the schema only declared required: ["mode"].

Add explicit "REQUIRED when mode='X'" markers to each conditionally-required
property description and a top-level "REQUIRED PARAMETERS: ..." summary for
each mode. Avoids anyOf/oneOf which break Anthropic, Fireworks, and
Kimi/Moonshot providers. Add TestPatchSchemaShape to lock the shape.

Fixes #15524

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Teknium: don't need 9 tests. Keep one invariant for 'per-mode required
params are documented in both description layers' and one that pins
required=[mode] with no anyOf/oneOf (prevents re-introducing the bug).
@teknium1 teknium1 merged commit 8e4f3ba into main May 8, 2026
14 of 15 checks passed
@teknium1 teknium1 deleted the hermes/hermes-8be3aa88 branch May 8, 2026 23:59
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-8be3aa88 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 7835 on HEAD, 7823 on base (🆕 +12)

🆕 New issues (19):

Rule Count
invalid-argument-type 17
unsupported-operator 2
First entries
tests/tools/test_file_tools.py:387: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["required"]` on object of type `str`
tests/tools/test_file_tools.py:377: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["properties"]` on object of type `str`
run_agent.py:6822: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
run_agent.py:11588: [invalid-argument-type] invalid-argument-type: Argument to function `apply_anthropic_cache_control` is incorrect: Expected `bool`, found `int | str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | dict[Unknown, Unknown]`
run_agent.py:12816: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
tests/tools/test_file_tools.py:380: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> str, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[str]]` cannot be called with key of type `Literal["patch"]` on object of type `list[str]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> str, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[str]]` cannot be called with key of type `Literal["path"]` on object of type `list[str]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["old_string"]` on object of type `str`
tests/tools/test_file_tools.py:380: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["patch"]` on object of type `str`
run_agent.py:8340: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | int | dict[Unknown, Unknown]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["path"]` on object of type `str`
run_agent.py:8525: [invalid-argument-type] invalid-argument-type: Argument to function `get_transport` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | int | dict[Unknown, Unknown]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> str, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[str]]` cannot be called with key of type `Literal["old_string"]` on object of type `list[str]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> LiteralString, (key: SupportsIndex | slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> str]` cannot be called with key of type `Literal["new_string"]` on object of type `str`
run_agent.py:12819: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown, Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
run_agent.py:8257: [invalid-argument-type] invalid-argument-type: Argument to bound method `ContextCompressor.update_model` is incorrect: Expected `int`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | int | dict[Unknown, Unknown]`
tests/tools/test_file_tools.py:379: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `Overload[(i: SupportsIndex, /) -> str, (s: slice[SupportsIndex | None, SupportsIndex | None, SupportsIndex | None], /) -> list[str]]` cannot be called with key of type `Literal["new_string"]` on object of type `list[str]`
tests/tools/test_file_tools.py:379: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["REQUIRED when mode='replace'"]` and `Unknown | str | list[str] | bool`
tests/tools/test_file_tools.py:380: [unsupported-operator] unsupported-operator: Operator `in` is not supported between objects of type `Literal["REQUIRED when mode='patch'"]` and `Unknown | str | list[str] | bool`

✅ Fixed issues (12):

Rule Count
invalid-argument-type 11
unresolved-attribute 1
First entries
run_agent.py:8525: [invalid-argument-type] invalid-argument-type: Argument to function `get_transport` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:8257: [invalid-argument-type] invalid-argument-type: Argument to bound method `ContextCompressor.update_model` is incorrect: Expected `int`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:12284: [invalid-argument-type] invalid-argument-type: Argument to function `normalize_usage` is incorrect: Expected `str | None`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:12816: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 4 union elements`
run_agent.py:8341: [invalid-argument-type] invalid-argument-type: Argument to function `get_provider_request_timeout` is incorrect: Expected `str | None`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:12819: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 4 union elements`
run_agent.py:11588: [invalid-argument-type] invalid-argument-type: Argument to function `apply_anthropic_cache_control` is incorrect: Expected `bool`, found `int | str | Unknown | ... omitted 3 union elements`
run_agent.py:8340: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:6822: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 4 union elements`
run_agent.py:8341: [invalid-argument-type] invalid-argument-type: Argument to function `get_provider_request_timeout` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`
run_agent.py:8019: [unresolved-attribute] unresolved-attribute: Attribute `strip` is not defined on `dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy`, `int & ~AlwaysFalsy`, `dict[Unknown, Unknown] & ~AlwaysFalsy` in union `(str & ~AlwaysFalsy) | (Unknown & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | ... omitted 4 union elements`
run_agent.py:8260: [invalid-argument-type] invalid-argument-type: Argument to bound method `ContextCompressor.update_model` is incorrect: Expected `str`, found `str | Unknown | dict[Unknown | str, Unknown | str | dict[str, str]] | ... omitted 3 union elements`

Unchanged: 4117 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/file File tools (read, write, patch, search) comp/tools Tool registry, model_tools, toolsets labels May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: patch tool conditionally required parameters are systematically omitted

3 participants