70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any, List, Optional, Tuple
|
|
|
|
|
|
def _pick_json_like_text(texts: List[str]) -> str:
|
|
candidates = [t.strip() for t in texts if isinstance(t, str) and t.strip()]
|
|
for t in reversed(candidates):
|
|
s = t.lstrip()
|
|
if s.startswith("{") or s.startswith("["):
|
|
return t
|
|
return candidates[-1] if candidates else ""
|
|
|
|
|
|
def extract_json_string_from_response(resp: Any) -> Tuple[str, Optional[str]]:
|
|
"""
|
|
Try to extract a JSON object either from structured 'json' content parts,
|
|
or from text content parts.
|
|
|
|
Returns: (raw_json_string, error_message_or_None)
|
|
"""
|
|
out_text = getattr(resp, "output_text", None)
|
|
if isinstance(out_text, str) and out_text.strip():
|
|
s = out_text.lstrip()
|
|
if s.startswith("{") or s.startswith("["):
|
|
return out_text, None
|
|
|
|
outputs = getattr(resp, "output", None) or []
|
|
texts: List[str] = []
|
|
|
|
for item in outputs:
|
|
content = getattr(item, "content", None)
|
|
if content is None and isinstance(item, dict):
|
|
content = item.get("content")
|
|
if not content:
|
|
continue
|
|
|
|
for part in content:
|
|
ptype = getattr(part, "type", None)
|
|
if ptype is None and isinstance(part, dict):
|
|
ptype = part.get("type")
|
|
|
|
if ptype == "refusal":
|
|
refusal_msg = getattr(part, "refusal", None)
|
|
if refusal_msg is None and isinstance(part, dict):
|
|
refusal_msg = part.get("refusal")
|
|
return "", f"Model refusal: {refusal_msg}"
|
|
|
|
j = getattr(part, "json", None)
|
|
if j is None and isinstance(part, dict):
|
|
j = part.get("json")
|
|
if j is not None:
|
|
try:
|
|
return json.dumps(j, ensure_ascii=False), None
|
|
except Exception as e:
|
|
return "", f"Failed to serialize json content part: {e}"
|
|
|
|
t = getattr(part, "text", None)
|
|
if t is None and isinstance(part, dict):
|
|
t = part.get("text")
|
|
if isinstance(t, str) and t.strip():
|
|
texts.append(t)
|
|
|
|
raw = _pick_json_like_text(texts)
|
|
if raw:
|
|
return raw, None
|
|
|
|
return "", "Empty output (no json/text content parts found)"
|