46 lines
1.4 KiB
Python
46 lines
1.4 KiB
Python
"""Validate JSON without inventing facts or repairing values from benchmark gold."""
|
|
|
|
import json
|
|
import re
|
|
from typing import Literal
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
|
|
class Claim(BaseModel):
|
|
model_config = ConfigDict(extra="forbid", strict=True)
|
|
person: str
|
|
title: str | None = None
|
|
year: int | None = None
|
|
start_year: int | None = None
|
|
keyword: str | None = None
|
|
category: str | None = None
|
|
document_author: str | None = None
|
|
record_id: str | None = None
|
|
work_type: str | None = None
|
|
work_label: str | None = None
|
|
|
|
|
|
class Answer(BaseModel):
|
|
model_config = ConfigDict(extra="forbid", strict=True)
|
|
answer: str
|
|
status: Literal["ok", "no_answer", "ambiguous", "error"]
|
|
claims: list[Claim]
|
|
source_urls: list[str]
|
|
count: int | None = None
|
|
|
|
|
|
def parse_answer(text: str) -> tuple[dict, bool]:
|
|
try:
|
|
data = json.loads(text.strip())
|
|
normalized = False
|
|
except json.JSONDecodeError:
|
|
# Exactly one complete JSON fence can be unwrapped. Never replace quotes,
|
|
# append missing braces, extract fragments or silently alter assertions.
|
|
blocks = re.findall(r"```(?:json)?\s*\n([\s\S]*?)```", text, flags=re.IGNORECASE)
|
|
if len(blocks) != 1:
|
|
raise
|
|
data = json.loads(blocks[0].strip())
|
|
normalized = True
|
|
parsed = Answer.model_validate(data)
|
|
return parsed.model_dump(exclude_none=True), normalized
|