Pydantic schema for sentiment results

The prompt forces the model to return a JSON object of a specific shape, but the prompt alone is not a contract. A contract lives in code. Pydantic gives you that contract. When the service returns an AnalyzeResponse, callers know exactly what fields exist, what types they are, and what ranges they live in.

Where Pydantic sits in the pipeline

Pydantic acts as the boundary contract between the LLM and the caller. Bad shapes fail fast inside the service.

models.py
python
from pydantic import BaseModel, Field
from typing import List, Optional, Literal


class SentimentResult(BaseModel):
    label: Literal["positive", "negative", "neutral"]
    score: float = Field(..., ge=-1.0, le=1.0)
    rationale: Optional[str] = None


class AnalyzeRequest(BaseModel):
    text: str = Field(..., min_length=1, max_length=20000)
    tasks: List[str] = Field(
        default_factory=lambda: ["sentiment", "entities", "keywords", "summary", "emotion"],
    )


class AnalyzeResponse(BaseModel):
    text: str
    tasks: List[str]
    sentiment: Optional[SentimentResult] = None
    errors: dict = Field(default_factory=dict)

Literal restricts label to three valid strings. Field(ge=-1.0, le=1.0) enforces the score range. min_length and max_length on the request stop empty strings and abusive 200KB payloads.

Literal turns a string field into a closed enum at the type level. If the model returns "positve" with a typo, Pydantic raises a ValidationError before the response ever leaves your service. A plain str would pass the typo straight through to the caller, who would have to do their own validation. Literal is the single cheapest safety net you can add to an LLM pipeline.

Quiz: Quiz

Loading practice…