"""Pydantic models for parsed financial candidates.""" from __future__ import annotations from enum import Enum from pydantic import BaseModel, Field class CandidateType(str, Enum): """Types of financial entities detected by the deterministic parser.""" TICKER = "ticker" CURRENCY = "currency" MONEY = "money" PERCENTAGE = "percentage" BASIS_POINTS = "basis_points" RANGE = "range" EPS = "eps" REVENUE = "revenue" DATE = "date" FISCAL_PERIOD = "fiscal_period" class PeriodAnnotation(BaseModel): """Optional period context for a parsed candidate (e.g., Q1, FY2025).""" period_type: str = Field(description="Type: quarter, year, fiscal_year, half") period_value: str = Field(description="Normalized period: Q1, Q2, H1, FY") year: int | None = Field(default=None, description="Calendar or fiscal year") class ParsedCandidate(BaseModel): """A single parsed financial entity with source offset and normalization. Stores both the literal text as it appeared in the source document and the normalized numeric value (if applicable). Exact character offsets allow downstream evidence linking back to the source. """ candidate_type: CandidateType = Field(description="Classification of the parsed entity") literal_value: str = Field(min_length=1, description="Exact text as it appears in source") normalized_value: float | None = Field(default=None, description="Normalized numeric value") unit: str | None = Field(default=None, description="Unit: USD, EUR, %, bps, etc.") start_char: int = Field(ge=0, description="Start character offset in source text") end_char: int = Field(gt=0, description="End character offset in source text (exclusive)") period: PeriodAnnotation | None = Field(default=None, description="Optional fiscal/calendar period")