Skip to content

fix(common): more robust JSON string loading#1350

Open
opposj wants to merge 2 commits intoagentscope-ai:mainfrom
opposj:main
Open

fix(common): more robust JSON string loading#1350
opposj wants to merge 2 commits intoagentscope-ai:mainfrom
opposj:main

Conversation

@opposj
Copy link
Copy Markdown

@opposj opposj commented Mar 19, 2026

AgentScope Version

1.0.17

Description

I encountered a JSON parsing issue while working with a locally deployed vLLM model fine-tuned on the Qwen-3 series. With tool calls resolved by Hermes, I obtained the following function argument (take an example):

args = '"{\\"query\\": \\"weak value amplification second order expansion average meter observable\\"}"' 

Currently, the JSON parsing in

def _json_loads_with_repair(
acts in a once-to-pass scheme, resulting in:

json.loads('"{\\"query\\": \\"weak value amplification second order expansion average meter observable\\"}"')
# -> '{"query": "weak value amplification second order expansion average meter observable"}'

Still a string and fails the isinstance check. However, it can actually be parsed via json.loads again:

json.loads('{"query": "weak value amplification second order expansion average meter observable"}')
# -> {'query': 'weak value amplification second order expansion average meter observable'}

Based on this observation, it is more robust to iteratively parse the raw string until a dict check is satisfied, and that's all about this commit. This commit is fully backward compatible and does not affect any other components, just providing a more robust JSON string parsing.

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with pre-commit run --all-files command
  • All tests are passing
  • Docstrings are in Google style
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

if isinstance(result, dict):
return result
result = repair_json(json_str, stream_stable=True)
while not isinstance(result := json.loads(result), dict):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@opposj This introduces an infinite loop — the loop body is ... (no-op), so if json.loads(result) never returns a dict, it will loop forever. What's the intention here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, an infinite loop is technically impossible due to the "reduction" behavior of json.loads. Given any object, json.loads will never return itself unchanged. A possible type from dict[str, Any] | list[Any] | str | float | int | bool | None is always guaranteed, or an exception is raised. For any return type that is not a dict or str, when fed into json.loads again, an exception occurs, and we have captured it correctly. For str, as mentioned before, json.loads will never infinitely resolve a string; every time a string is fed, the output is shortened until an exception is hit. Overall, as our goal is to return a valid dict, iteratively checking for a potentially "nested" string is a more robust method with no risk introduced :)

I have added a simple example that demonstrates this "nested" case in the comment of the changed code.

@DavdGao DavdGao added the state: under review The pull request is under review label Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state: under review The pull request is under review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants