curated_feed.config

Configuration loading.

Settings are grouped into the same sections the TOML file uses, so a value’s name in code and in the file are one and the same.

All filesystem locations come from the config file (or CLI flags), never from literals in this package: the policy, corpus and state directories belong to the deployment, not to the code.

Attributes

Exceptions

ConfigError

Raised when configuration is missing or malformed.

Classes

Section

Base for every settings section: strict, and immutable once loaded.

Paths

Where everything lives. Relative entries resolve against the config file.

FetchSettings

Collecting candidates. These are other people's servers: stay polite.

PaywallSettings

Checking whether a candidate's page can actually be read.

ScoreSettings

Which scorer runs, and what it is allowed to spend.

Adjustments

The weights of scoring specification §2.3, and their thresholds.

PolicySettings

The arithmetic the model never sees.

PublishSettings

The public surface. Deliberately bland: no name, employer or project.

DeploySettings

Where the built feed is uploaded, and by what.

Config

Resolved settings for a run.

Functions

find_config_file(→ pathlib.Path)

Locate the config file, searching start and its parents.

load_config(→ Config)

Load settings from TOML. Paths are resolved relative to the config file.

Module Contents

curated_feed.config.DEFAULT_CONFIG_FILENAME = 'config.toml'
exception curated_feed.config.ConfigError[source]

Bases: Exception

Raised when configuration is missing or malformed.

class curated_feed.config.Section(/, **data: Any)[source]

Bases: pydantic.BaseModel

Base for every settings section: strict, and immutable once loaded.

model_config

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class curated_feed.config.Paths(/, **data: Any)[source]

Bases: Section

Where everything lives. Relative entries resolve against the config file.

The scoring prompt is deliberately absent. It is the engine’s contract with its own parser rather than a runner’s choice, so it ships with the package and no configuration can name it; –scoring-prompt overrides it for the length of one command. A config that still carries a prompt key is rejected rather than ignored, which is what makes an un-migrated runner say so instead of quietly scoring against the wrong document.

sources: pathlib.Path
corpus_dir: pathlib.Path
policy: pathlib.Path
state_dir: pathlib.Path
build_dir: pathlib.Path
class curated_feed.config.FetchSettings(/, **data: Any)[source]

Bases: Section

Collecting candidates. These are other people’s servers: stay polite.

lookback_days: int
summary_max_chars: int
concurrency: int
timeout_seconds: float
max_retries: int
user_agent: str = 'curated-feed/0.1'
class curated_feed.config.PaywallSettings(/, **data: Any)[source]

Bases: Section

Checking whether a candidate’s page can actually be read.

Off by default: turning it on makes a request per graded article to somebody else’s site, so it is a runner’s decision rather than a default. Deliberately more cautious than [fetch] — these are article pages, and there are more of them than there are feeds.

enabled: bool = False
concurrency: int
timeout_seconds: float
max_retries: int
max_checks: int
user_agent: str = ''
class curated_feed.config.ScoreSettings(/, **data: Any)[source]

Bases: Section

Which scorer runs, and what it is allowed to spend.

provider: str = 'stub'
model: str = ''
max_tokens: int
effort: str = ''
timeout_seconds: float
stub_items: int
class curated_feed.config.Adjustments(/, **data: Any)[source]

Bases: Section

The weights of scoring specification §2.3, and their thresholds.

recurrence: float = 1.0
recurrence_cap_sources: int
staleness: float = -1.0
staleness_hours: int
penalty: float = -1.0
class curated_feed.config.PolicySettings(/, **data: Any)[source]

Bases: Section

The arithmetic the model never sees.

floor: float
max_items: int
preference_cap: int
rationale_mode: Literal['none', 'minimal'] = 'minimal'
adjustments: Adjustments
class curated_feed.config.PublishSettings(/, **data: Any)[source]

Bases: Section

The public surface. Deliberately bland: no name, employer or project.

window: int
title: str = 'Curated feed'
author: str = 'curated-feed'
site_url: str = ''
path_prefix: str = ''
class curated_feed.config.DeploySettings(/, **data: Any)[source]

Bases: Section

Where the built feed is uploaded, and by what.

No credential belongs here. The deploy tool resolves its own from the environment, exactly as the scorer does, so the repository never holds one.

provider: str = 'none'
project: str = ''
compatibility_date: str
timeout_seconds: float
class curated_feed.config.Config(/, **data: Any)[source]

Bases: Section

Resolved settings for a run.

paths: Paths
fetch: FetchSettings
paywall: PaywallSettings
score: ScoreSettings
policy: PolicySettings
publish: PublishSettings
deploy: DeploySettings
with_overrides(**sections: dict[str, Any] | None) → Config[source]

Return a copy with any non-None override applied, section by section.

Overrides arrive from command-line flags, where “not given” is None and must leave the configured value alone.

curated_feed.config.find_config_file(start: pathlib.Path | None = None) → pathlib.Path[source]

Locate the config file, searching start and its parents.

curated_feed.config.load_config(config_path: pathlib.Path | None = None) → Config[source]

Load settings from TOML. Paths are resolved relative to the config file.