| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 |
- """LLM 回复 JSON 解析工具。"""
- from __future__ import annotations
- import json
- import re
- from typing import Any
- from app.gap_script_demand.exceptions import GapScriptDemandError
- def strip_markdown_fence(text: str) -> str:
- raw = text.strip()
- if not raw.startswith("```"):
- return raw
- raw = re.sub(r"^```(?:json)?\s*", "", raw, flags=re.IGNORECASE)
- raw = re.sub(r"\s*```\s*$", "", raw)
- return raw.strip()
- def extract_json_object(text: str) -> dict[str, Any]:
- """从 LLM 回复中提取第一个 JSON 对象(容忍尾部多余文字)。"""
- raw = strip_markdown_fence(text)
- decoder = json.JSONDecoder()
- def _try_decode(candidate: str) -> dict[str, Any] | None:
- stripped = candidate.strip()
- if not stripped:
- return None
- try:
- parsed = json.loads(stripped)
- if isinstance(parsed, dict):
- return parsed
- except json.JSONDecodeError:
- pass
- try:
- parsed, _end = decoder.raw_decode(stripped)
- if isinstance(parsed, dict):
- return parsed
- except json.JSONDecodeError:
- pass
- return None
- direct = _try_decode(raw)
- if direct is not None:
- return direct
- start = 0
- while True:
- start = raw.find("{", start)
- if start == -1:
- break
- decoded = _try_decode(raw[start:])
- if decoded is not None:
- return decoded
- start += 1
- raise GapScriptDemandError("llm output is not json object")
|