Fix array extraction after unmatched brace
This commit is contained in:
@@ -82,7 +82,6 @@ def _top_level_bracket_arrays(text: str) -> list[str]:
|
|||||||
i, n = 0, len(text)
|
i, n = 0, len(text)
|
||||||
outer_in_str = False
|
outer_in_str = False
|
||||||
outer_esc = False
|
outer_esc = False
|
||||||
brace_depth = 0
|
|
||||||
while i < n:
|
while i < n:
|
||||||
ch = text[i]
|
ch = text[i]
|
||||||
if outer_in_str:
|
if outer_in_str:
|
||||||
@@ -99,14 +98,36 @@ def _top_level_bracket_arrays(text: str) -> list[str]:
|
|||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
if ch == "{":
|
if ch == "{":
|
||||||
brace_depth += 1
|
# Skip balanced objects, including arrays nested inside them. An
|
||||||
|
# unmatched brace may be ordinary prose, though, so do not let it
|
||||||
|
# poison the rest of the scan and hide a later valid array.
|
||||||
|
depth = 0
|
||||||
|
in_str = False
|
||||||
|
esc = False
|
||||||
|
j = i
|
||||||
|
while j < n:
|
||||||
|
current = text[j]
|
||||||
|
if in_str:
|
||||||
|
if esc:
|
||||||
|
esc = False
|
||||||
|
elif current == "\\":
|
||||||
|
esc = True
|
||||||
|
elif current == '"':
|
||||||
|
in_str = False
|
||||||
|
elif current == '"':
|
||||||
|
in_str = True
|
||||||
|
elif current == "{":
|
||||||
|
depth += 1
|
||||||
|
elif current == "}":
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0:
|
||||||
|
i = j + 1
|
||||||
|
break
|
||||||
|
j += 1
|
||||||
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
if ch == "}":
|
if ch != "[":
|
||||||
brace_depth = max(0, brace_depth - 1)
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
if ch != "[" or brace_depth:
|
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ class TestTopLevelBracketArrays:
|
|||||||
def test_bracket_inside_quoted_prose_is_ignored(self) -> None:
|
def test_bracket_inside_quoted_prose_is_ignored(self) -> None:
|
||||||
assert _top_level_bracket_arrays('label is "set it to [x]" done') == []
|
assert _top_level_bracket_arrays('label is "set it to [x]" done') == []
|
||||||
|
|
||||||
|
def test_unmatched_prose_brace_does_not_hide_later_array(self) -> None:
|
||||||
|
text = "Explanation uses {placeholder, final answer [1, 2]"
|
||||||
|
assert _top_level_bracket_arrays(text) == ["[1, 2]"]
|
||||||
|
|
||||||
|
|
||||||
class TestExtractJsonTolerantFallback:
|
class TestExtractJsonTolerantFallback:
|
||||||
"""extract_json — json_repair fallback for malformed non-OpenAI output."""
|
"""extract_json — json_repair fallback for malformed non-OpenAI output."""
|
||||||
@@ -215,3 +219,7 @@ class TestExtractJsonArray:
|
|||||||
def test_nested_object_array_not_confused_with_array_answer(self) -> None:
|
def test_nested_object_array_not_confused_with_array_answer(self) -> None:
|
||||||
text = '{"items": [1, 2, 3]}'
|
text = '{"items": [1, 2, 3]}'
|
||||||
assert extract_json_array(text) is None
|
assert extract_json_array(text) is None
|
||||||
|
|
||||||
|
def test_unmatched_prose_brace_before_valid_array(self) -> None:
|
||||||
|
text = "Explanation uses {placeholder, final answer [1, 2]"
|
||||||
|
assert extract_json_array(text) == [1, 2]
|
||||||
|
|||||||
Reference in New Issue
Block a user