Skip to content

Commit afff847

Browse files
committed
fix: enhance JSON healing to handle concatenated JSON objects
1 parent 2c2ee1c commit afff847

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

custom_components/llmvision/providers.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,26 @@ def _is_value_boundary(ch: str) -> bool:
383383
json.loads(healed)
384384
return healed
385385
except json.JSONDecodeError:
386-
return text
386+
pass
387+
388+
decoder = json.JSONDecoder()
389+
idx = 0
390+
while idx < len(healed):
391+
while idx < len(healed) and healed[idx].isspace():
392+
idx += 1
393+
if idx >= len(healed):
394+
break
395+
if healed[idx] not in "{[":
396+
idx += 1
397+
continue
398+
try:
399+
_, end_idx = decoder.raw_decode(healed, idx)
400+
except json.JSONDecodeError:
401+
idx += 1
402+
continue
403+
return healed[idx:end_idx]
404+
405+
return text
387406

388407

389408
class Provider(ABC):

tests/test_providers.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,25 @@ def test_heal_json_balances_open_containers(self, mock_hass):
302302
assert isinstance(parsed["events"], list)
303303
assert parsed["events"][0]["name"] == "door"
304304

305+
def test_heal_json_concatenated_json_objects(self, mock_hass):
306+
"""Test heal_json keeps the first JSON object from a concatenated stream."""
307+
with patch("custom_components.llmvision.providers.async_get_clientsession"):
308+
request = Request(mock_hass, "test", 1000, 0.5)
309+
310+
broken = (
311+
'{"title": "Person at front door", "description": "A person stands on the porch."}'
312+
'{"title": "No activity", "description": "The scene shows an empty driveway and patio with no notable objects."}'
313+
'{"title": "Delivery", "description": "A delivery person is seen standing near the entrance."}'
314+
'{"title": "Person in garden", "description": "Two people stand at the front door."}'
315+
'{"title": "No activity", "description": "The'
316+
)
317+
318+
healed = request.heal_json(broken)
319+
parsed = json.loads(healed)
320+
321+
assert parsed["title"] == "Person at front door"
322+
assert parsed["description"] == "A person stands on the porch."
323+
305324
def test_heal_json_non_string(self, mock_hass):
306325
"""Test heal_json with non-string input."""
307326
with patch("custom_components.llmvision.providers.async_get_clientsession"):

0 commit comments

Comments
 (0)