fix(openclaw): robust JSON repair for small LLM responses
All checks were successful
E2E Health Check / e2e-health (push) Successful in 19s
All checks were successful
E2E Health Check / e2e-health (push) Successful in 19s
This commit is contained in:
@@ -942,31 +942,61 @@ class OpenClawService:
|
||||
# =========================================================================
|
||||
|
||||
def _extract_json_from_response(self, text: str) -> str | None:
|
||||
"""從 LLM 回應中提取 JSON"""
|
||||
# 嘗試直接解析
|
||||
"""從 LLM 回應中提取 JSON (含啟發式修補)"""
|
||||
# 0. 清理開頭結尾空白
|
||||
text = text.strip()
|
||||
if not text:
|
||||
return None
|
||||
|
||||
# 1. 嘗試直接解析
|
||||
try:
|
||||
json.loads(text)
|
||||
return text
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 嘗試從 markdown code block 提取
|
||||
# 2. 嘗試從 markdown code block 提取
|
||||
patterns = [
|
||||
r"```json\s*([\s\S]*?)\s*```",
|
||||
r"```\s*([\s\S]*?)\s*```",
|
||||
r"\{[\s\S]*\}",
|
||||
r"(\{[\s\S]*\})", # 貪婪匹配最大括號對
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, text)
|
||||
if match:
|
||||
candidate = match.group(1) if "```" in pattern else match.group(0)
|
||||
candidate = match.group(1) if "(" in pattern else match.group(0)
|
||||
candidate = candidate.strip()
|
||||
try:
|
||||
json.loads(candidate)
|
||||
return candidate
|
||||
except json.JSONDecodeError:
|
||||
# 3. 啟發式修補: 如果結尾缺少括號,嘗試補齊
|
||||
if candidate.startswith("{") and not candidate.endswith("}"):
|
||||
for i in range(1, 5): # 嘗試補 1-5 個括號/引號
|
||||
try:
|
||||
repaired = candidate + '"' * (i-1) + "}" * i
|
||||
json.loads(repaired)
|
||||
logger.info("json_repaired_heuristically", level=i)
|
||||
return repaired
|
||||
except:
|
||||
continue
|
||||
continue
|
||||
|
||||
# 4. 極端情況: 找出最後一個有效 key
|
||||
if "{" in text:
|
||||
start_idx = text.find("{")
|
||||
candidate = text[start_idx:]
|
||||
# 暴力去除非法尾綴 (如 \t\t...)
|
||||
candidate = re.sub(r"[ \t\r\n]+$", "", candidate)
|
||||
if not candidate.endswith("}"):
|
||||
candidate += '"}' # 嘗試最簡單的閉合
|
||||
try:
|
||||
json.loads(candidate)
|
||||
return candidate
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def _parse_analysis_result(self, raw_response: str) -> OpenClawDecision | None:
|
||||
|
||||
Reference in New Issue
Block a user