curated_feed.models¶
Data contracts for the curation pipeline.
Three of these are boundaries rather than conveniences. Candidate is the corpus record schema, consumed by later stages and by manual evaluation sessions. Response is what comes back from the model, which is untrusted input and is validated as such. SelectionResult is the record of what was chosen and why.
Validation here covers shape and range only. Conflicts between records — the same article both dropped and listed as a duplicate, or a candidate the response never mentions — are resolved during selection, where they can be recorded and reported rather than merely rejected.
Attributes¶
Classes¶
One normalised feed item. |
|
The model's reading of one article, as specified in policy/prompt.md. |
|
A day's judgements: what was dropped, and what was graded. |
|
One weighted term of a score, kept beside the reason it was applied. |
|
A selected article, with the arithmetic that put it there. |
|
The state/selections/DATE.json artifact. |
|
One line of state/paywall.jsonl: whether one article can be read. |
|
One line of state/published.jsonl, and one entry of the feed. |
|
What the last render produced, so the next one can tell if it changed. |
|
What the last upload sent, so an unchanged feed is not sent again. |
Module Contents¶
- curated_feed.models.MIN_CONSEQUENCE = 1¶
- curated_feed.models.MAX_CONSEQUENCE = 3¶
- curated_feed.models.TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%SZ'¶
- curated_feed.models.VERDICT_FREE = 'free'¶
- curated_feed.models.VERDICT_PAYWALLED = 'paywalled'¶
- curated_feed.models.VERDICT_UNKNOWN = 'unknown'¶
- class curated_feed.models.Candidate(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne normalised feed item.
The field order below is the corpus record schema, and JSON is written in it. Changing the fields, their order or their meaning is a deliberate act.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- published_at: datetime.datetime | None¶
- fetched_at: datetime.datetime¶
- class curated_feed.models.Graded(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelThe model’s reading of one article, as specified in policy/prompt.md.
Every field is a judgement that requires having read the article. Nothing here is arithmetic, and nothing here knows the floor, the item cap or the ordering — those belong to selection.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class curated_feed.models.Response(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA day’s judgements: what was dropped, and what was graded.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property accounted_indices: dict[int, int][source]¶
How often the response mentions each candidate number.
The coverage invariant wants exactly one mention per candidate. Counting rather than testing membership is what makes both failures visible: a count of zero is an article never considered, and a count above one is an article claimed twice.
- class curated_feed.models.Adjustment(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne weighted term of a score, kept beside the reason it was applied.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class curated_feed.models.Selection(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA selected article, with the arithmetic that put it there.
rationale is the only field published; everything else exists for tuning and stays in the private selection log.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- adjustments: list[Adjustment] = None¶
- class curated_feed.models.SelectionResult(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelThe state/selections/DATE.json artifact.
Rejects are grouped by reason code rather than listed one by one, which keeps the artifact small while preserving a complete account of the day. anomalies records what the response got wrong and selection had to resolve; an empty list is the normal case, and a growing one is a policy problem.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- run_date: datetime.date¶
- class curated_feed.models.Verdict(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne line of state/paywall.jsonl: whether one article can be read.
Keyed by candidate id and written once. The log is append-only and outlives a run, so detail carries why a verdict was reached — “HTTP 403”, “no markup” — which is the whole audit trail for a day whose feed looks short.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- verdict: Literal['free', 'paywalled', 'unknown']¶
- checked_at: datetime.datetime¶
- class curated_feed.models.PublishedEntry(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne line of state/published.jsonl, and one entry of the feed.
Denormalised on purpose: rendering is then a function of this log alone, and cannot be changed by a later edit to the corpus or the selection.
summary is the source’s own description, carried here for the same reason everything else is. It defaults to empty because the log is append-only and outlives its schema: lines written before the field existed have to stay readable, and they render as they always did.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- published_at: datetime.datetime | None¶
- run_date: datetime.date¶
- class curated_feed.models.FeedMeta(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelWhat the last render produced, so the next one can tell if it changed.
The feed-level <updated> moves only when the content hash does. Stamping the run time on every render makes a reader treat every item as modified.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- updated: datetime.datetime¶
- class curated_feed.models.DeployMeta(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelWhat the last upload sent, so an unchanged feed is not sent again.
Written only after a real upload succeeds. A failed one leaves the previous hash in place, which is what makes the next run retry rather than assume.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- deployed_at: datetime.datetime¶