Skip to content

fix(tools): wrap bare scalars in single-element list for array-typed args#19717

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-a9c932d1
May 4, 2026
Merged

fix(tools): wrap bare scalars in single-element list for array-typed args#19717
teknium1 merged 1 commit into
mainfrom
hermes/hermes-a9c932d1

Conversation

@teknium1

@teknium1 teknium1 commented May 4, 2026

Copy link
Copy Markdown
Contributor

Open-weight models (DeepSeek, Qwen, GLM) sometimes emit {"urls": "https://a.com"} where the schema expects type: array. The call was JSON-valid but semantically wrong — the tool then failed with a confusing type error. coerce_tool_args now wraps non-list, non-null values into a single-element list.

Changes

  • model_tools.coerce_tool_args: +24 LOC — new array-wrap branch that runs strings through _coerce_value first (so JSON-encoded arrays and nullable "null" still work), then falls back to [value] wrap for bare scalars. None preserved.
  • 7 new tests in tests/run_agent/test_tool_arg_coercion.py covering bare string / int / dict / JSON-encoded / list-passthrough / None / existing-test-updated.

Validation

Before After
{"urls": "https://a.com"} on array field passed through as string → tool error ["https://a.com"]
{"urls": '["a","b"]'} parsed to ["a","b"] parsed to ["a","b"] (unchanged)
{"stages": "null"} nullable array None None (unchanged)
{"items": None} None None (unchanged — we don't second-guess deliberate nulls)
tests/run_agent/ 1206 passing 1213 passing (+7)

Relation to #19652

Salvaged from #19652 by @NikolayGusev-astra. The broader validate-then-repair layer in that PR had several problems: it duplicated the existing coerce_tool_args path for string→array, mis-classified old_string as a path field (would silently corrupt patch calls), hardcoded offset=1 / limit=500 defaults that only fit read_file, and prepended a [validate-then-repair: ...] prefix to every tool result — breaking any downstream consumer that JSON-parses tool output. The genuinely new capability was bare-scalar-as-array wrapping, which this PR implements directly inside the existing coercion path in ~24 LOC.

Co-authored-by: Nikolay Gusev ngusev@astralinux.ru

…args

Open-weight models (DeepSeek, Qwen, GLM) sometimes emit tool calls like
`{"urls": "https://a.com"}` when the tool schema declares
`type: array`.  The call was JSON-valid but semantically wrong, and
`coerce_tool_args` would pass the bare string through — the tool then
failed with a confusing type error.

`coerce_tool_args` now wraps non-list, non-null values in a
single-element list when the schema declares `array`.  Strings still go
through `_coerce_value` first so JSON-encoded arrays
(`'["a","b"]'`) parse correctly and nullable `"null"` still
becomes `None`.  `None` itself is preserved — tools with sensible
defaults already handle it, and we don't want to silently mask a
deliberate null.

Salvaged from #19652 (NikolayGusev-astra) — the broader validate-then-
repair layer had several issues (duplicated existing coercion,
mis-classified `old_string` as a path field, prepended non-JSON
prefixes to tool results that break downstream JSON parsing, hardcoded
offset/limit defaults unsuitable for non-read_file tools).  The one
genuinely new capability is wrapping bare scalars, which is implemented
here directly inside the existing coercion path.

Co-authored-by: Nikolay Gusev <ngusev@astralinux.ru>
@teknium1 teknium1 merged commit fdf9343 into main May 4, 2026
8 of 11 checks passed
@teknium1 teknium1 deleted the hermes/hermes-a9c932d1 branch May 4, 2026 12:00
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets labels May 4, 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants