Source code for qfa.domain.clustering_models
"""Domain models for the coding-trend table used in hierarchical analysis.
These models are free of any import from :mod:`qfa.domain.models` so that
``models.py`` can safely import :class:`CodingTrendTable` without creating
a circular dependency. The chunk model (:class:`~qfa.domain.chunk_models.Chunk`),
which DOES import :class:`~qfa.domain.models.FeedbackRecordModel`, lives in
:mod:`qfa.domain.chunk_models` instead.
All models are immutable (frozen) Pydantic models per ADR-001.
"""
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field
#: Supported granularities for the coding-trend table's period bucketing.
#:
#: Lives in :mod:`qfa.domain.clustering_models` (rather than alongside
#: the bucketing logic in :mod:`qfa.services.coding_trends`) so that
#: settings and request models — which sit outside ``qfa.services`` —
#: can reference the type without creating a layering loop.
TrendPeriod = Literal["day", "week", "month"]
[docs]
class CodingTrendCell(BaseModel):
"""One (code, period, count) cell of the coding-trend table."""
model_config = ConfigDict(frozen=True)
code: str = Field(description="Coding label (per the coding framework).")
period: str = Field(
description=(
"Time period bucket label. Shape depends on the granularity:"
" ``YYYY-MM-DD`` for day, ``YYYY-Www`` (ISO week) for week,"
" ``YYYY-MM`` for month."
)
)
count: int = Field(
ge=0, description="Number of records coded with this code in this period."
)
[docs]
class CodingTrendTable(BaseModel):
"""Deterministic, non-LLM count of codes over time periods.
Built from feedback-record metadata; fed into the reduce prompt as a
faithfulness anchor. When metadata is absent the table is omitted
(``None`` at call sites), and reduce degrades to text-only synthesis.
"""
model_config = ConfigDict(frozen=True)
periods: tuple[str, ...] = Field(
description="Sorted, de-duplicated period buckets covered by the table.",
)
cells: tuple[CodingTrendCell, ...] = Field(
description="Per-(code, period) counts. Empty when no codes were found.",
)