Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
## 2024-05-18 - Improve Swagger UI DX with swagger_ui_parameters
**Learning:** The default Swagger UI configuration for FastAPI applications often lacks helpful features like request duration display or dark-themed syntax highlighting, and requires users to manually click "Try it out" for every endpoint.
**Action:** Always configure `swagger_ui_parameters` in the `FastAPI()` instantiation with options like `{"displayRequestDuration": True, "syntaxHighlight.theme": "monokai", "tryItOutEnabled": True}` to significantly improve the Developer Experience (DX) when interacting with the API documentation.
## 2026-07-03 - Optimize OpenAPI Examples and Required Fields in Pydantic V2
**Learning:** In Pydantic V2 and FastAPI, adding an explicit ellipsis `...` to fields without defaults is unnecessary as they are automatically required. Furthermore, when defining OpenAPI examples, using `json_schema_extra={"example": <value>}` avoids `PydanticDeprecatedSince20` warnings associated with passing `example` directly to `Field()` and ensures standard Swagger UI functionality.
**Action:** Remove unnecessary `...` on required fields and use `json_schema_extra={"example": <value>}` for singular OpenAPI examples to significantly improve Developer Experience (DX) while maintaining strict `PYTHONWARNINGS` CI compatibility.
4 changes: 1 addition & 3 deletions src/newsdom_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,7 @@ def _validate_pdf_structure(pdf_bytes: bytes) -> None:
tags=["Parser"],
)
async def parse(
file: Annotated[
UploadFile, File(..., description="The newspaper PDF file to parse.")
],
file: Annotated[UploadFile, File(description="The newspaper PDF file to parse.")],
) -> ParseResponse:
"""Parse an uploaded PDF into the canonical DOM response model."""

Expand Down
53 changes: 41 additions & 12 deletions src/newsdom_api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,31 @@
class BoundingBox(BaseModel):
"""Axis-aligned bounding box expressed in page coordinates."""

x0: float = Field(..., description="Leftmost X coordinate of the bounding box.")
y0: float = Field(..., description="Topmost Y coordinate of the bounding box.")
x1: float = Field(..., description="Rightmost X coordinate of the bounding box.")
y1: float = Field(..., description="Bottommost Y coordinate of the bounding box.")
x0: float = Field(
description="Leftmost X coordinate of the bounding box.",
json_schema_extra={"example": 72.5},
)
y0: float = Field(
description="Topmost Y coordinate of the bounding box.",
json_schema_extra={"example": 100.0},
)
x1: float = Field(
description="Rightmost X coordinate of the bounding box.",
json_schema_extra={"example": 400.5},
)
y1: float = Field(
description="Bottommost Y coordinate of the bounding box.",
json_schema_extra={"example": 250.0},
)


class CaptionNode(BaseModel):
"""Caption text associated with an image or figure."""

text: str = Field(..., description="Text content of the caption.")
text: str = Field(
description="Text content of the caption.",
json_schema_extra={"example": "Figure 1: Economic growth chart for Q3."},
)
bbox: Optional[BoundingBox] = Field(
default=None,
description="Bounding box of the caption when parser coordinates are available.",
Expand All @@ -29,7 +44,10 @@ class CaptionNode(BaseModel):
class ImageNode(BaseModel):
"""Image metadata preserved in the canonical page structure."""

path: str = Field(..., description="Relative path to the extracted image asset.")
path: str = Field(
description="Relative path to the extracted image asset.",
json_schema_extra={"example": "images/page1_img1.jpg"},
)
media_type: str = Field(
default="image",
description="Media type label for the extracted image node.",
Expand All @@ -52,9 +70,13 @@ class ArticleNode(BaseModel):
"""Article-level grouping of headline, body blocks, and related media."""

article_id: str = Field(
..., description="Stable identifier for the article within the parsed document."
description="Stable identifier for the article within the parsed document.",
json_schema_extra={"example": "article-1a2b"},
)
headline: str = Field(
description="Primary headline text for the article.",
json_schema_extra={"example": "Local Elections Conclude Successfully"},
)
headline: str = Field(..., description="Primary headline text for the article.")
bbox: Optional[BoundingBox] = Field(
default=None,
description="Bounding box enclosing the article when parser coordinates are available.",
Expand All @@ -81,7 +103,8 @@ class PageNode(BaseModel):
"""Single parsed page including article, ad, and header groupings."""

page_number: int = Field(
..., description="One-based page number from the parsed PDF."
description="One-based page number from the parsed PDF.",
json_schema_extra={"example": 1},
)
width: Optional[float] = Field(
default=None,
Expand Down Expand Up @@ -117,10 +140,14 @@ class ParseQuality(BaseModel):
"""Quality metadata describing parser provenance and warnings."""

status: str = Field(
default="success", description="Parsing operation status indicator."
default="success",
description="Parsing operation status indicator.",
json_schema_extra={"example": "success"},
)
parser: str = Field(
default="mineru", description="The underlying engine used to parse the PDF."
default="mineru",
description="The underlying engine used to parse the PDF.",
json_schema_extra={"example": "mineru"},
)
warnings: List[str] = Field(
default_factory=list,
Expand All @@ -132,7 +159,8 @@ class ParseResponse(BaseModel):
"""Top-level API response for a parsed document."""

document_id: str = Field(
..., description="Unique identifier for the parsed document."
description="Unique identifier for the parsed document.",
json_schema_extra={"example": "doc-8f9e1a"},
)
pages: List[PageNode] = Field(
default_factory=list,
Expand All @@ -150,4 +178,5 @@ class HealthResponse(BaseModel):
status: str = Field(
default="ok",
description="Current operational status of the service.",
json_schema_extra={"example": "ok"},
)
Loading