Skip to content

fix(tools): normalize JSON-encoded tool args from local LLMs#536

Open
JessicaMulein wants to merge 7 commits into
cecli-dev:mainfrom
Digital-Defiance:fix/tool-json-normalization
Open

fix(tools): normalize JSON-encoded tool args from local LLMs#536
JessicaMulein wants to merge 7 commits into
cecli-dev:mainfrom
Digital-Defiance:fix/tool-json-normalization

Conversation

@JessicaMulein
Copy link
Copy Markdown

Summary

Local / Ollama models sometimes pass tool parameters as JSON strings instead of native arrays/objects (e.g. show for ReadRange, tasks for UpdateTodoList). That caused tool failures or opaque ToolErrors during headless and desktop-hosted runs.

This PR adds shared coercion helpers and uses them in ReadRange and UpdateTodoList so we accept:

  • a real list or dict
  • a JSON string for the whole array
  • a list mixing dicts and JSON-string elements (common quirk)

Changes

  • cecli/tools/utils/helpers.py: normalize_json_array(), coerce_dict_item() with clear ToolError messages
  • cecli/tools/read_range.py: normalize_show_ops() before path resolution
  • cecli/tools/update_todo_list.py: normalize_task_items() for agent todo sync
  • Tests: tests/tools/test_get_lines.py (string show JSON), new tests/tools/test_update_todo_list.py

Motivation

BrightVision dogfood (Vision HTTP + local Qwen/Llama) hit this in E2E and real sessions: the model emitted "show": "[{...}]" or stringified task rows. Interactive cecli may see it less often; headless tool loops see it regularly.

Test plan

  • pytest tests/tools/test_get_lines.py tests/tools/test_update_todo_list.py
  • Manual: headless turn with ReadRange / UpdateTodoList where the model double-encodes JSON args

Notes

  • No behavior change when the model already sends well-typed JSON arrays.
  • Invalid JSON still fails fast with a descriptive ToolError.

JessicaMulein and others added 7 commits May 28, 2026 07:29
Mirror ReadRange format_output ToolError handling; fall back to plain
task text when brace-prefixed strings are not JSON. Tighten tests and
fix import order for pre-commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match CI pre-commit (black --line-length 100 --preview). Mirror ReadRange
Invalid Tool JSON handling in UpdateTodoList.format_output; add tests for
mixed JSON-string task rows and malformed tool arguments.

Co-authored-by: Cursor <cursoragent@cursor.com>
Some models emit tool array args as one string per character (e.g. UpdateTodoList
tasks). Rejoin and parse before normalize_task_items / ReadRange show ops.

Co-authored-by: Cursor <cursoragent@cursor.com>
- normalize_search_operations for char-split and bare pattern strings
- _repair_local_model_json_text / _try_parse_json_value for ReadRange
  show and other double-encoded arrays (literal newline after ':')
- Tests: test_grep.py, test_get_lines.py, test_update_todo_list.py

Co-authored-by: Cursor <cursoragent@cursor.com>
DeepSeek and similar models emit {…}{}{…} tool args; merge into one
object before dispatch instead of triplicate ls/Git/Grep calls. Fix Grep
format_output tool_footer TypeError on parse errors. Tests in
test_tool_arguments.py.

Co-authored-by: Cursor <cursoragent@cursor.com>
dwash96 pushed a commit that referenced this pull request May 31, 2026
@dwash96 dwash96 mentioned this pull request May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant