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 = None
summary_max_chars: int = None
concurrency: int = None
timeout_seconds: float = None
max_retries: int = None
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 = None
timeout_seconds: float = None
max_retries: int = None
max_checks: int = None
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 = None
effort: str = ''
timeout_seconds: float = None
stub_items: int = None
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 = None
staleness: float = -1.0
staleness_hours: int = None
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 = None
preference_cap: int = None
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 = None
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 = None
timeout_seconds: float = None
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.