fix: parse nested JSON in LLM responses + runtime bug fixes#301
Closed
lbartoszcze wants to merge 1 commit intomainfrom
Closed
fix: parse nested JSON in LLM responses + runtime bug fixes#301lbartoszcze wants to merge 1 commit intomainfrom
lbartoszcze wants to merge 1 commit intomainfrom
Conversation
The _parse_action regex r'\{[^{}]*"tool"[^{}]*\}' rejected any JSON with
nested braces, silently dropping all params when the LLM returned nested
objects (the common case). Agents could decide WHAT to do but lost HOW —
a shell:bash action would lose its command, email:send would lose recipients.
Replaced with balanced-brace extraction that correctly handles nested params
like {"tool": "shell:bash", "params": {"command": "ls"}, "reasoning": "..."}.
Also fixes:
- cognition.py: Vertex AI used sync AnthropicVertex in async think(),
blocking the event loop. Replaced with AsyncAnthropicVertex + await.
- orchestrator.py: _message() and _broadcast() accessed _message_boxes
without existence check, causing KeyError when agent's queue wasn't
pre-initialized. Added on-demand queue creation.
- request.py: aiohttp call to Linear API had no timeout, risking
indefinite hangs. Added 30s ClientTimeout.
57 tests covering parse logic, orchestrator messaging, cognition engine,
and skill smoke tests. CI workflow restored.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes 4 runtime bugs that affect every agent running on Singularity, with 57 tests proving correctness.
1.
_parse_actionsilently drops params on nested JSON (CRITICAL)The bug: The regex
r'\{[^{}]*"tool"[^{}]*\}'incognition.py:_parse_action()uses[^{}]*which rejects any JSON containing nested braces. When the LLM returns the common case:{"tool": "shell:bash", "params": {"command": "git status"}, "reasoning": "check repo"}...the regex fails to match because
{"command": "git status"}contains braces. The fallback strips ALL params, so the agent knows what to do but loses how to do it.The fix: Replaced regex with balanced-brace extraction (
_extract_tool_json) that scans for{, counts depth to find the matching}, and attemptsjson.loads. Correctly handles nested objects, arrays, and braces in strings.19 tests cover flat JSON, nested params, deeply nested objects, markdown code blocks, arrays, missing keys, invalid JSON, and fallback paths.
2. Vertex AI uses sync client in async
think()(HIGH)The bug:
cognition.pyimportsAnthropicVertex(sync) instead ofAsyncAnthropicVertex, and callsself.llm.messages.create()withoutawaitin the asyncthink()method. This blocks the entire event loop during inference.The fix: Import
AsyncAnthropicVertex, addawaitto the Vertex call path.3. Orchestrator
_message()/_broadcast()KeyError (HIGH)The bug: Both methods access
_message_boxes[agent_id]without checking if the key exists. If an agent's message box wasn't pre-initialized (e.g., loaded from a previous session), this crashes withKeyError.The fix: Guard both methods to create the queue on demand:
if agent_id not in _message_boxes: _message_boxes[agent_id] = asyncio.Queue().7 tests cover missing boxes, existing boxes, non-existent agents, self-skip, and dead agent filtering.
4.
request.pyLinear API call has no timeout (MEDIUM)The bug:
aiohttp.ClientSession()in_create_linear_ticket()has no timeout, risking indefinite hangs if the Linear API is unreachable.The fix: Added
aiohttp.ClientTimeout(total=30).Files changed
singularity/cognition.py_extract_tool_json()method,AsyncAnthropicVerteximport,awaiton Vertex callsingularity/skills/orchestrator.py_message_boxesaccess in_message()and_broadcast()singularity/skills/request.pypyproject.tomlpytest-timeoutto dev deps, pytest config.github/workflows/ci.ymltests/test_parse_action.pytests/test_orchestrator_messaging.pytests/test_cognition_engine.pytests/test_smoke.pyTest plan
pytest tests/ -v --timeout=30)ruff check --select F821,F811,F601)🤖 Generated with Claude Code