Configuration models¶
The full FoodScholarConfig schema — every section, field, type, and default. For an
example-first walkthrough and recipes, see the
Configuration guide; this page is the exhaustive
field reference, generated from the models themselves.
Top level¶
.. py:pydantic_model:: FoodScholarConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
annotate (foodscholar.config.AnnotateConfig) <foodscholar.config.FoodScholarConfig.annotate>
- py:
obj:
corpus (foodscholar.config.CorpusConfig) <foodscholar.config.FoodScholarConfig.corpus>
- py:
obj:
layer_a (foodscholar.config.LayerAConfig) <foodscholar.config.FoodScholarConfig.layer_a>
- py:
obj:
layer_b (foodscholar.config.LayerBConfig) <foodscholar.config.FoodScholarConfig.layer_b>
- py:
obj:
layer_c (foodscholar.config.LayerCConfig) <foodscholar.config.FoodScholarConfig.layer_c>
- py:
obj:
llm (foodscholar.config.LLMConfig | None) <foodscholar.config.FoodScholarConfig.llm>
- py:
obj:
ontology (foodscholar.config.OntologyConfig | None) <foodscholar.config.FoodScholarConfig.ontology>
- py:
obj:
storage (foodscholar.config.StorageConfig) <foodscholar.config.FoodScholarConfig.storage>
Loading¶
.. py:function:: resolve_config(config)
- module:
foodscholar.config
Normalize any supported config source into a
FoodScholarConfig.Accepts a YAML file path, a Python dict, or an already-validated config object. ${ENV} substitution runs over dicts and strings too, so in-code configs can carry env placeholders the same way YAML can.
.. py:function:: load_config(path)
- module:
foodscholar.config
Storage¶
.. py:pydantic_model:: StorageConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
card_store (foodscholar.config.CardStoreConfig) <foodscholar.config.StorageConfig.card_store>
- py:
obj:
chunk_store (foodscholar.config.ChunkStoreConfig) <foodscholar.config.StorageConfig.chunk_store>
- py:
obj:
graph_store (foodscholar.config.GraphStoreConfig) <foodscholar.config.StorageConfig.graph_store>
.. py:pydantic_model:: ChunkStoreConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
api_key (str | None) <foodscholar.config.ChunkStoreConfig.api_key>
- py:
obj:
backend (Literal['elastic', 'memory']) <foodscholar.config.ChunkStoreConfig.backend>
- py:
obj:
bulk_size (int) <foodscholar.config.ChunkStoreConfig.bulk_size>
- py:
obj:
index (str | None) <foodscholar.config.ChunkStoreConfig.index>
- py:
obj:
password (str | None) <foodscholar.config.ChunkStoreConfig.password>
- py:
obj:
url (str | None) <foodscholar.config.ChunkStoreConfig.url>
- py:
obj:
username (str | None) <foodscholar.config.ChunkStoreConfig.username>
.. py:pydantic_field:: ChunkStoreConfig.api_key
- module:
foodscholar.config
- type:
str | None
- value:
None
.. py:pydantic_field:: ChunkStoreConfig.password
- module:
foodscholar.config
- type:
str | None
- value:
None
.. py:pydantic_field:: ChunkStoreConfig.bulk_size
- module:
foodscholar.config
- type:
int
- value:
500
.. py:pydantic_model:: GraphStoreConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
backend (Literal['neo4j', 'memory']) <foodscholar.config.GraphStoreConfig.backend>
- py:
obj:
password (str | None) <foodscholar.config.GraphStoreConfig.password>
- py:
obj:
url (str | None) <foodscholar.config.GraphStoreConfig.url>
- py:
obj:
user (str | None) <foodscholar.config.GraphStoreConfig.user>
.. py:pydantic_field:: GraphStoreConfig.password
- module:
foodscholar.config
- type:
str | None
- value:
None
LLM¶
.. py:pydantic_model:: LLMConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelLLM client configuration: a primary provider plus an ordered fallback chain. The chain is tried in order; each entry is attempted only if all earlier ones errored (timeout, rate limit, auth, service down).
- Fields:
- py:
obj:
fallbacks (list[foodscholar.config.ProviderConfig]) <foodscholar.config.LLMConfig.fallbacks>
- py:
obj:
max_retries (int) <foodscholar.config.LLMConfig.max_retries>
- py:
obj:
primary (foodscholar.config.ProviderConfig) <foodscholar.config.LLMConfig.primary>
- py:
obj:
timeout_s (float) <foodscholar.config.LLMConfig.timeout_s>
.. py:pydantic_model:: ProviderConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelOne LLM provider + model.
API keys can be supplied either in this section (
api_key:— useful for in-code configs and Docker secrets) or via the provider’s standard environment variable (ANTHROPIC_API_KEY,OPENAI_API_KEY,OPENROUTER_API_KEY,GROQ_API_KEY,GEMINI_API_KEY). The config value wins when both are set. Ollama needs no key — just a running daemon athost.- Fields:
- py:
obj:
api_key (str | None) <foodscholar.config.ProviderConfig.api_key>
- py:
obj:
host (str | None) <foodscholar.config.ProviderConfig.host>
- py:
obj:
model (str) <foodscholar.config.ProviderConfig.model>
- py:
obj:
provider (Literal['anthropic', 'openai', 'openrouter', 'groq', 'gemini', 'ollama']) <foodscholar.config.ProviderConfig.provider>
Corpus & ontology¶
.. py:pydantic_model:: CorpusConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
annotated_snapshot_path (pathlib.Path | None) <foodscholar.config.CorpusConfig.annotated_snapshot_path>
- py:
obj:
chunks_path (pathlib.Path) <foodscholar.config.CorpusConfig.chunks_path>
- py:
obj:
ignore_source_types (list[Literal['abstract', 'textbook', 'guide']]) <foodscholar.config.CorpusConfig.ignore_source_types>
.. py:pydantic_field:: CorpusConfig.annotated_snapshot_path
- module:
foodscholar.config
- type:
Path | None
- value:
None
.. py:pydantic_field:: CorpusConfig.ignore_source_types
- module:
foodscholar.config
- type:
list[SourceType]
- optional:
.. py:pydantic_model:: OntologyConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
cache_path (pathlib.Path | None) <foodscholar.config.OntologyConfig.cache_path>
- py:
obj:
foodon_path (pathlib.Path) <foodscholar.config.OntologyConfig.foodon_path>
- py:
obj:
include_imports (bool) <foodscholar.config.OntologyConfig.include_imports>
- py:
obj:
prefix_filter (list[str] | None) <foodscholar.config.OntologyConfig.prefix_filter>
.. py:pydantic_field:: OntologyConfig.prefix_filter
- module:
foodscholar.config
- type:
list[str] | None
- value:
[‘FOODON:’]
Annotation¶
.. py:pydantic_model:: AnnotateConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
batch_size (int) <foodscholar.config.AnnotateConfig.batch_size>
- py:
obj:
embedder (str) <foodscholar.config.AnnotateConfig.embedder>
- py:
obj:
gliner (foodscholar.config.GLinerConfig) <foodscholar.config.AnnotateConfig.gliner>
- py:
obj:
linker (foodscholar.config.LinkerConfig) <foodscholar.config.AnnotateConfig.linker>
- py:
obj:
ner (Literal['gliner']) <foodscholar.config.AnnotateConfig.ner>
.. py:pydantic_field:: AnnotateConfig.ner
- module:
foodscholar.config
- type:
Literal[‘gliner’]
- value:
‘gliner’
.. py:pydantic_field:: AnnotateConfig.batch_size
- module:
foodscholar.config
- type:
int
- value:
16
.. py:pydantic_model:: GLinerConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelGLiNER-bio NER configuration. Defaults match the validated prototype.
- Fields:
- py:
obj:
batch_size (int) <foodscholar.config.GLinerConfig.batch_size>
- py:
obj:
flat_ner (bool) <foodscholar.config.GLinerConfig.flat_ner>
- py:
obj:
labels (list[str]) <foodscholar.config.GLinerConfig.labels>
- py:
obj:
max_length (int) <foodscholar.config.GLinerConfig.max_length>
- py:
obj:
model_id (str) <foodscholar.config.GLinerConfig.model_id>
- py:
obj:
threshold (float) <foodscholar.config.GLinerConfig.threshold>
.. py:pydantic_model:: LinkerConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelEntity-linking configuration.
Backend =
hnsw(local hnswlib index, default) orelastic(ES dense_vector, opt-in). The HNSW index is built on first use from the loaded FoodOn ontology and cached tonel_index_path/nel_metadata_path(auto-derived when those are left unset).- Fields:
- py:
obj:
es_index (str | None) <foodscholar.config.LinkerConfig.es_index>
- py:
obj:
nel_backend (Literal['hnsw', 'elastic']) <foodscholar.config.LinkerConfig.nel_backend>
- py:
obj:
nel_encoder (Literal['sapbert', 'biolord', 'minilm', 'mpnet']) <foodscholar.config.LinkerConfig.nel_encoder>
- py:
obj:
nel_index_path (pathlib.Path | None) <foodscholar.config.LinkerConfig.nel_index_path>
- py:
obj:
nel_metadata_path (pathlib.Path | None) <foodscholar.config.LinkerConfig.nel_metadata_path>
- py:
obj:
nel_min_sim (float) <foodscholar.config.LinkerConfig.nel_min_sim>
- py:
obj:
nel_top_k (int) <foodscholar.config.LinkerConfig.nel_top_k>
.. py:pydantic_field:: LinkerConfig.nel_index_path
- module:
foodscholar.config
- type:
Path | None
- value:
None
.. py:pydantic_field:: LinkerConfig.nel_metadata_path
- module:
foodscholar.config
- type:
Path | None
- value:
None
Layer A¶
.. py:pydantic_model:: LayerAConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
alias_shelves (bool) <foodscholar.config.LayerAConfig.alias_shelves>
- py:
obj:
backbone_max_children (int) <foodscholar.config.LayerAConfig.backbone_max_children>
- py:
obj:
blacklist_terms (list[str]) <foodscholar.config.LayerAConfig.blacklist_terms>
- py:
obj:
bottom_up_grouping (foodscholar.config.BottomUpGroupingConfig) <foodscholar.config.LayerAConfig.bottom_up_grouping>
- py:
obj:
collapse_single_child_chains (bool) <foodscholar.config.LayerAConfig.collapse_single_child_chains>
- py:
obj:
facet_overrides (dict[Literal['foods', 'health', 'sustainability', 'dietary_patterns', 'allergies', 'nutrients'], foodscholar.config.FacetConfig]) <foodscholar.config.LayerAConfig.facet_overrides>
- py:
obj:
facets (list[Literal['foods', 'health', 'sustainability', 'dietary_patterns', 'allergies', 'nutrients']]) <foodscholar.config.LayerAConfig.facets>
- py:
obj:
link_blocklist (list[foodscholar.config.LinkBlocklistEntry]) <foodscholar.config.LayerAConfig.link_blocklist>
- py:
obj:
max_depth (int) <foodscholar.config.LayerAConfig.max_depth>
- py:
obj:
min_link_confidence (float) <foodscholar.config.LayerAConfig.min_link_confidence>
- py:
obj:
min_support (int) <foodscholar.config.LayerAConfig.min_support>
- py:
obj:
projection (Literal['backbone', 'prune']) <foodscholar.config.LayerAConfig.projection>
- py:
obj:
semantic_consolidation (foodscholar.config.SemanticConsolidationConfig) <foodscholar.config.LayerAConfig.semantic_consolidation>
- py:
obj:
umbrella_direct_share_max (float) <foodscholar.config.LayerAConfig.umbrella_direct_share_max>
- py:
obj:
umbrella_lifted_share_min (float) <foodscholar.config.LayerAConfig.umbrella_lifted_share_min>
- py:
obj:
umbrella_min_count (int) <foodscholar.config.LayerAConfig.umbrella_min_count>
.. py:pydantic_field:: LayerAConfig.umbrella_direct_share_max
- module:
foodscholar.config
- type:
float
- value:
0.1
.. py:pydantic_field:: LayerAConfig.umbrella_lifted_share_min
- module:
foodscholar.config
- type:
float
- value:
0.85
.. py:pydantic_field:: LayerAConfig.umbrella_min_count
- module:
foodscholar.config
- type:
int
- value:
25
.. py:pydantic_field:: LayerAConfig.link_blocklist
- module:
foodscholar.config
- type:
list[LinkBlocklistEntry]
- optional:
.. py:pydantic_field:: LayerAConfig.projection :module: foodscholar.config :type: Literal[‘backbone’, ‘prune’] :value: ‘backbone’
.. py:pydantic_field:: LayerAConfig.backbone_max_children :module: foodscholar.config :type: int :value: 12
.. py:pydantic_field:: LayerAConfig.alias_shelves :module: foodscholar.config :type: bool :value: True
.. py:pydantic_field:: LayerAConfig.facet_overrides :module: foodscholar.config :type: dict[Facet, FacetConfig] :optional:
.. py:pydantic_field:: LayerAConfig.semantic_consolidation :module: foodscholar.config :type: SemanticConsolidationConfig :optional:
.. py:pydantic_field:: LayerAConfig.bottom_up_grouping :module: foodscholar.config :type: BottomUpGroupingConfig :optional:
Layer B¶
.. py:pydantic_model:: LayerBConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelLayer B (theme discovery) — dual-pass + merge per the brief.
See
layer_b_construction_brief.md§5 for the full knob list and the accompanying plan for the v1 decisions (Leiden default, LLM labels by default, embedded-fraction gate, etc.).algorithm="bertopic"swaps the Pass-1 discovery backend; Leiden remains the default.- Fields:
- py:
obj:
algorithm (Literal['leiden', 'bertopic']) <foodscholar.config.LayerBConfig.algorithm>
- py:
obj:
audit (foodscholar.config.LayerBAuditConfig) <foodscholar.config.LayerBConfig.audit>
- py:
obj:
bertopic (foodscholar.config.BertopicConfig) <foodscholar.config.LayerBConfig.bertopic>
- py:
obj:
global_similarity_max_chunks (int) <foodscholar.config.LayerBConfig.global_similarity_max_chunks>
- py:
obj:
labeling (foodscholar.config.LabelingConfig) <foodscholar.config.LayerBConfig.labeling>
- py:
obj:
leiden (foodscholar.config.LeidenConfig) <foodscholar.config.LayerBConfig.leiden>
- py:
obj:
merge (foodscholar.config.MergeConfig) <foodscholar.config.LayerBConfig.merge>
- py:
obj:
min_chunks_per_shelf (int) <foodscholar.config.LayerBConfig.min_chunks_per_shelf>
- py:
obj:
min_embedded_fraction (float) <foodscholar.config.LayerBConfig.min_embedded_fraction>
- py:
obj:
pass1_mode (Literal['global', 'per_shelf']) <foodscholar.config.LayerBConfig.pass1_mode>
- py:
obj:
relatedness (foodscholar.config.RelatednessConfig) <foodscholar.config.LayerBConfig.relatedness>
- py:
obj:
scope (Literal['direct', 'subtree']) <foodscholar.config.LayerBConfig.scope>
- py:
obj:
similarity (foodscholar.config.SimilarityConfig) <foodscholar.config.LayerBConfig.similarity>
.. py:pydantic_field:: LayerBConfig.algorithm
- module:
foodscholar.config
- type:
Literal[‘leiden’, ‘bertopic’]
- value:
‘leiden’
.. py:pydantic_field:: LayerBConfig.scope
- module:
foodscholar.config
- type:
Literal[‘direct’, ‘subtree’]
- value:
‘direct’
.. py:pydantic_field:: LayerBConfig.min_embedded_fraction
- module:
foodscholar.config
- type:
float
- value:
0.8
.. py:pydantic_field:: LayerBConfig.pass1_mode
- module:
foodscholar.config
- type:
Literal[‘global’, ‘per_shelf’]
- value:
‘per_shelf’
.. py:pydantic_field:: LayerBConfig.global_similarity_max_chunks
- module:
foodscholar.config
- type:
int
- value:
50000
.. py:pydantic_model:: SimilarityConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelPass 1 (similarity) graph + algorithm knobs.
algorithmis restricted to"leiden"in v1 — HDBSCAN is documented as a fallback in the brief but cut from v1 per the implementation plan.- Fields:
- py:
obj:
algorithm (Literal['leiden']) <foodscholar.config.SimilarityConfig.algorithm>
- py:
obj:
edge_threshold (float) <foodscholar.config.SimilarityConfig.edge_threshold>
- py:
obj:
knn_k (int) <foodscholar.config.SimilarityConfig.knn_k>
- py:
obj:
require_mutual (bool) <foodscholar.config.SimilarityConfig.require_mutual>
.. py:pydantic_model:: RelatednessConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelPass 2 (relatedness) graph knobs.
tau_strict: minimum entity-link confidence to participate in edges.min_shared_ids: edge created iff >= this many shared FoodOn IDs.max_doc_frequency: entities appearing in > this fraction of the shelf’s chunks are dropped (they carry no discriminative signal).always_exclude_iris: never-edge-creators. The umbrella class FOODON:00001002 (‘food product’) is the default exclusion — it survived Layer A and gets ancestor-propagated onto almost every chunk.
- Fields:
- py:
obj:
algorithm (Literal['leiden']) <foodscholar.config.RelatednessConfig.algorithm>
- py:
obj:
always_exclude_iris (list[str]) <foodscholar.config.RelatednessConfig.always_exclude_iris>
- py:
obj:
max_doc_frequency (float) <foodscholar.config.RelatednessConfig.max_doc_frequency>
- py:
obj:
min_shared_ids (int) <foodscholar.config.RelatednessConfig.min_shared_ids>
- py:
obj:
tau_strict (float) <foodscholar.config.RelatednessConfig.tau_strict>
.. py:pydantic_model:: LeidenConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelShared by both passes.
random_stateis the determinism contract — same chunks + same seed = identical theme assignment across runs.- Fields:
- py:
obj:
min_community_size (int) <foodscholar.config.LeidenConfig.min_community_size>
- py:
obj:
n_iterations (int) <foodscholar.config.LeidenConfig.n_iterations>
- py:
obj:
random_state (int) <foodscholar.config.LeidenConfig.random_state>
- py:
obj:
resolution (float) <foodscholar.config.LeidenConfig.resolution>
.. py:pydantic_model:: MergeConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelGreedy pair-assignment merge.
combined_similarity = chunk_weight * J(chunks) + entity_weight * J(entities)per (sim_i, rel_j); pairs at or abovededupe_thresholdcollapse intodiscovery_pass="merged"themes.- Fields:
- py:
obj:
chunk_weight (float) <foodscholar.config.MergeConfig.chunk_weight>
- py:
obj:
dedupe_threshold (float) <foodscholar.config.MergeConfig.dedupe_threshold>
- py:
obj:
entity_weight (float) <foodscholar.config.MergeConfig.entity_weight>
.. py:pydantic_model:: LabelingConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModelTheme labeling.
"llm"is v1 default — navigation labels need to read well and per-run cost is ~$0.60."keyword"(pure c-TF-IDF) is a free deterministic fallback. c-TF-IDF is always computed and fed to the LLM as keyword context.- Fields:
- py:
obj:
llm_max_tokens (int) <foodscholar.config.LabelingConfig.llm_max_tokens>
- py:
obj:
strategy (Literal['keyword', 'llm']) <foodscholar.config.LabelingConfig.strategy>
- py:
obj:
top_keywords (int) <foodscholar.config.LabelingConfig.top_keywords>
Layer C¶
.. py:pydantic_model:: LayerCConfig
- module:
foodscholar.config
Bases: :py:class:
~pydantic.main.BaseModel- Fields:
- py:
obj:
benchmark_out_dir (str) <foodscholar.config.LayerCConfig.benchmark_out_dir>
- py:
obj:
grounding_check (Literal['strict', 'lenient', 'off']) <foodscholar.config.LayerCConfig.grounding_check>
- py:
obj:
group_char_budget (int) <foodscholar.config.LayerCConfig.group_char_budget>
- py:
obj:
llm_model (str) <foodscholar.config.LayerCConfig.llm_model>
- py:
obj:
map_reduce_threshold (int) <foodscholar.config.LayerCConfig.map_reduce_threshold>
- py:
obj:
max_summary_chars (int) <foodscholar.config.LayerCConfig.max_summary_chars>
- py:
obj:
prompt_version (str) <foodscholar.config.LayerCConfig.prompt_version>
- py:
obj:
safety_sensitive_facets (list[Literal['foods', 'health', 'sustainability', 'dietary_patterns', 'allergies', 'nutrients']]) <foodscholar.config.LayerCConfig.safety_sensitive_facets>
- py:
obj:
sample_size (int) <foodscholar.config.LayerCConfig.sample_size>
- py:
obj:
stage1_method (Literal['lexrank', 'lsa', 'luhn', 'textrank', 'nltk_freq']) <foodscholar.config.LayerCConfig.stage1_method>
- py:
obj:
stage1_sentences (int) <foodscholar.config.LayerCConfig.stage1_sentences>
.. py:pydantic_field:: LayerCConfig.stage1_sentences
- module:
foodscholar.config
- type:
int
- value:
8
.. py:pydantic_field:: LayerCConfig.map_reduce_threshold
- module:
foodscholar.config
- type:
int
- value:
400
.. py:pydantic_field:: LayerCConfig.group_char_budget
- module:
foodscholar.config
- type:
int
- value:
20000
.. py:pydantic_field:: LayerCConfig.max_summary_chars
- module:
foodscholar.config
- type:
int
- value:
4000