llm_client: 진짜 JSON 앞에 군더더기 { 한 줄이 붙은 응답 처리 (고객사 실측)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
d8718c858f
commit
c577d5cb93
@@ -109,6 +109,29 @@ def _repair_json(text: str) -> str:
|
||||
return "".join(out)
|
||||
|
||||
|
||||
def _first_object(content: str) -> dict | None:
|
||||
"""`{` 가 나오는 자리마다 객체 하나를 읽어 본다 — 앞에 군더더기 `{` 나 서문이 붙은 경우.
|
||||
|
||||
고객사 실측: 모델이 `{` 한 줄을 먼저 쓰고 그 다음 줄에 진짜 JSON 을 냈다.
|
||||
"""
|
||||
dec = json.JSONDecoder()
|
||||
first = content.find("{")
|
||||
i = first
|
||||
while i >= 0:
|
||||
# 첫 `{` 이후로는 그 사이가 공백뿐인 자리(군더더기 `{` 바로 다음)만 본다 —
|
||||
# 값 안에 중첩된 `{"kind": ...}` 를 최상위 객체로 오인하지 않게
|
||||
if i != first and content[first + 1 : i].strip(" \t\r\n{"):
|
||||
break
|
||||
try:
|
||||
data, _ = dec.raw_decode(content, i)
|
||||
if isinstance(data, dict) and data:
|
||||
return data
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
i = content.find("{", i + 1)
|
||||
return None
|
||||
|
||||
|
||||
def _extract_json(content: str) -> dict:
|
||||
"""모델이 코드펜스/서문을 붙이거나 문법이 약간 틀린 JSON 을 내는 경우까지 감안해 뽑아낸다.
|
||||
|
||||
@@ -117,6 +140,9 @@ def _extract_json(content: str) -> dict:
|
||||
try:
|
||||
return json.loads(content)
|
||||
except json.JSONDecodeError as first:
|
||||
found = _first_object(content)
|
||||
if found is not None:
|
||||
return found
|
||||
s, e = content.find("{"), content.rfind("}")
|
||||
candidate = content[s : e + 1] if (s >= 0 and e > s) else content[s:] if s >= 0 else content
|
||||
for attempt in (candidate, _repair_json(candidate)):
|
||||
@@ -126,6 +152,15 @@ def _extract_json(content: str) -> dict:
|
||||
return data
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
# 군더더기 `{` 뒤에 오는 객체가 문법까지 틀린 경우: 두 번째 `{` 부터 복구
|
||||
s2 = content.find("{", s + 1) if s >= 0 else -1
|
||||
if s2 > 0:
|
||||
try:
|
||||
data = json.loads(_repair_json(content[s2 : e + 1] if e > s2 else content[s2:]))
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
try:
|
||||
d = settings.data_llm_jobs / "_badjson"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user