bead 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bead/__init__.py +11 -0
- bead/__main__.py +11 -0
- bead/active_learning/__init__.py +15 -0
- bead/active_learning/config.py +231 -0
- bead/active_learning/loop.py +566 -0
- bead/active_learning/models/__init__.py +24 -0
- bead/active_learning/models/base.py +852 -0
- bead/active_learning/models/binary.py +910 -0
- bead/active_learning/models/categorical.py +943 -0
- bead/active_learning/models/cloze.py +862 -0
- bead/active_learning/models/forced_choice.py +956 -0
- bead/active_learning/models/free_text.py +773 -0
- bead/active_learning/models/lora.py +365 -0
- bead/active_learning/models/magnitude.py +835 -0
- bead/active_learning/models/multi_select.py +795 -0
- bead/active_learning/models/ordinal_scale.py +811 -0
- bead/active_learning/models/peft_adapter.py +155 -0
- bead/active_learning/models/random_effects.py +639 -0
- bead/active_learning/selection.py +354 -0
- bead/active_learning/strategies.py +391 -0
- bead/active_learning/trainers/__init__.py +26 -0
- bead/active_learning/trainers/base.py +210 -0
- bead/active_learning/trainers/data_collator.py +172 -0
- bead/active_learning/trainers/dataset_utils.py +261 -0
- bead/active_learning/trainers/huggingface.py +304 -0
- bead/active_learning/trainers/lightning.py +324 -0
- bead/active_learning/trainers/metrics.py +424 -0
- bead/active_learning/trainers/mixed_effects.py +551 -0
- bead/active_learning/trainers/model_wrapper.py +509 -0
- bead/active_learning/trainers/registry.py +104 -0
- bead/adapters/__init__.py +11 -0
- bead/adapters/huggingface.py +61 -0
- bead/behavioral/__init__.py +116 -0
- bead/behavioral/analytics.py +646 -0
- bead/behavioral/extraction.py +343 -0
- bead/behavioral/merging.py +343 -0
- bead/cli/__init__.py +11 -0
- bead/cli/active_learning.py +513 -0
- bead/cli/active_learning_commands.py +779 -0
- bead/cli/completion.py +359 -0
- bead/cli/config.py +624 -0
- bead/cli/constraint_builders.py +286 -0
- bead/cli/deployment.py +859 -0
- bead/cli/deployment_trials.py +493 -0
- bead/cli/deployment_ui.py +332 -0
- bead/cli/display.py +378 -0
- bead/cli/items.py +960 -0
- bead/cli/items_factories.py +776 -0
- bead/cli/list_constraints.py +714 -0
- bead/cli/lists.py +490 -0
- bead/cli/main.py +430 -0
- bead/cli/models.py +877 -0
- bead/cli/resource_loaders.py +621 -0
- bead/cli/resources.py +1036 -0
- bead/cli/shell.py +356 -0
- bead/cli/simulate.py +840 -0
- bead/cli/templates.py +1158 -0
- bead/cli/training.py +1080 -0
- bead/cli/utils.py +614 -0
- bead/cli/workflow.py +1273 -0
- bead/config/__init__.py +68 -0
- bead/config/active_learning.py +1009 -0
- bead/config/config.py +192 -0
- bead/config/defaults.py +118 -0
- bead/config/deployment.py +217 -0
- bead/config/env.py +147 -0
- bead/config/item.py +45 -0
- bead/config/list.py +193 -0
- bead/config/loader.py +149 -0
- bead/config/logging.py +42 -0
- bead/config/model.py +49 -0
- bead/config/paths.py +46 -0
- bead/config/profiles.py +320 -0
- bead/config/resources.py +47 -0
- bead/config/serialization.py +210 -0
- bead/config/simulation.py +206 -0
- bead/config/template.py +238 -0
- bead/config/validation.py +267 -0
- bead/data/__init__.py +65 -0
- bead/data/base.py +87 -0
- bead/data/identifiers.py +97 -0
- bead/data/language_codes.py +61 -0
- bead/data/metadata.py +270 -0
- bead/data/range.py +123 -0
- bead/data/repository.py +358 -0
- bead/data/serialization.py +249 -0
- bead/data/timestamps.py +89 -0
- bead/data/validation.py +349 -0
- bead/data_collection/__init__.py +11 -0
- bead/data_collection/jatos.py +223 -0
- bead/data_collection/merger.py +154 -0
- bead/data_collection/prolific.py +198 -0
- bead/deployment/__init__.py +5 -0
- bead/deployment/distribution.py +402 -0
- bead/deployment/jatos/__init__.py +1 -0
- bead/deployment/jatos/api.py +200 -0
- bead/deployment/jatos/exporter.py +210 -0
- bead/deployment/jspsych/__init__.py +9 -0
- bead/deployment/jspsych/biome.json +44 -0
- bead/deployment/jspsych/config.py +411 -0
- bead/deployment/jspsych/generator.py +598 -0
- bead/deployment/jspsych/package.json +51 -0
- bead/deployment/jspsych/pnpm-lock.yaml +2141 -0
- bead/deployment/jspsych/randomizer.py +299 -0
- bead/deployment/jspsych/src/lib/list-distributor.test.ts +327 -0
- bead/deployment/jspsych/src/lib/list-distributor.ts +1282 -0
- bead/deployment/jspsych/src/lib/randomizer.test.ts +232 -0
- bead/deployment/jspsych/src/lib/randomizer.ts +367 -0
- bead/deployment/jspsych/src/plugins/cloze-dropdown.ts +252 -0
- bead/deployment/jspsych/src/plugins/forced-choice.ts +265 -0
- bead/deployment/jspsych/src/plugins/plugins.test.ts +141 -0
- bead/deployment/jspsych/src/plugins/rating.ts +248 -0
- bead/deployment/jspsych/src/slopit/index.ts +9 -0
- bead/deployment/jspsych/src/types/jatos.d.ts +256 -0
- bead/deployment/jspsych/src/types/jspsych.d.ts +228 -0
- bead/deployment/jspsych/templates/experiment.css +1 -0
- bead/deployment/jspsych/templates/experiment.js.template +289 -0
- bead/deployment/jspsych/templates/index.html +51 -0
- bead/deployment/jspsych/templates/randomizer.js +241 -0
- bead/deployment/jspsych/templates/randomizer.js.template +313 -0
- bead/deployment/jspsych/trials.py +723 -0
- bead/deployment/jspsych/tsconfig.json +23 -0
- bead/deployment/jspsych/tsup.config.ts +30 -0
- bead/deployment/jspsych/ui/__init__.py +1 -0
- bead/deployment/jspsych/ui/components.py +383 -0
- bead/deployment/jspsych/ui/styles.py +411 -0
- bead/dsl/__init__.py +80 -0
- bead/dsl/ast.py +168 -0
- bead/dsl/context.py +178 -0
- bead/dsl/errors.py +71 -0
- bead/dsl/evaluator.py +570 -0
- bead/dsl/grammar.lark +81 -0
- bead/dsl/parser.py +231 -0
- bead/dsl/stdlib.py +929 -0
- bead/evaluation/__init__.py +13 -0
- bead/evaluation/convergence.py +485 -0
- bead/evaluation/interannotator.py +398 -0
- bead/items/__init__.py +40 -0
- bead/items/adapters/__init__.py +70 -0
- bead/items/adapters/anthropic.py +224 -0
- bead/items/adapters/api_utils.py +167 -0
- bead/items/adapters/base.py +216 -0
- bead/items/adapters/google.py +259 -0
- bead/items/adapters/huggingface.py +1074 -0
- bead/items/adapters/openai.py +323 -0
- bead/items/adapters/registry.py +202 -0
- bead/items/adapters/sentence_transformers.py +224 -0
- bead/items/adapters/togetherai.py +309 -0
- bead/items/binary.py +515 -0
- bead/items/cache.py +558 -0
- bead/items/categorical.py +593 -0
- bead/items/cloze.py +757 -0
- bead/items/constructor.py +784 -0
- bead/items/forced_choice.py +413 -0
- bead/items/free_text.py +681 -0
- bead/items/generation.py +432 -0
- bead/items/item.py +396 -0
- bead/items/item_template.py +787 -0
- bead/items/magnitude.py +573 -0
- bead/items/multi_select.py +621 -0
- bead/items/ordinal_scale.py +569 -0
- bead/items/scoring.py +448 -0
- bead/items/validation.py +723 -0
- bead/lists/__init__.py +30 -0
- bead/lists/balancer.py +263 -0
- bead/lists/constraints.py +1067 -0
- bead/lists/experiment_list.py +286 -0
- bead/lists/list_collection.py +378 -0
- bead/lists/partitioner.py +1141 -0
- bead/lists/stratification.py +254 -0
- bead/participants/__init__.py +73 -0
- bead/participants/collection.py +699 -0
- bead/participants/merging.py +312 -0
- bead/participants/metadata_spec.py +491 -0
- bead/participants/models.py +276 -0
- bead/resources/__init__.py +29 -0
- bead/resources/adapters/__init__.py +19 -0
- bead/resources/adapters/base.py +104 -0
- bead/resources/adapters/cache.py +128 -0
- bead/resources/adapters/glazing.py +508 -0
- bead/resources/adapters/registry.py +117 -0
- bead/resources/adapters/unimorph.py +796 -0
- bead/resources/classification.py +856 -0
- bead/resources/constraint_builders.py +329 -0
- bead/resources/constraints.py +165 -0
- bead/resources/lexical_item.py +223 -0
- bead/resources/lexicon.py +744 -0
- bead/resources/loaders.py +209 -0
- bead/resources/template.py +441 -0
- bead/resources/template_collection.py +707 -0
- bead/resources/template_generation.py +349 -0
- bead/simulation/__init__.py +29 -0
- bead/simulation/annotators/__init__.py +15 -0
- bead/simulation/annotators/base.py +175 -0
- bead/simulation/annotators/distance_based.py +135 -0
- bead/simulation/annotators/lm_based.py +114 -0
- bead/simulation/annotators/oracle.py +182 -0
- bead/simulation/annotators/random.py +181 -0
- bead/simulation/dsl_extension/__init__.py +3 -0
- bead/simulation/noise_models/__init__.py +13 -0
- bead/simulation/noise_models/base.py +42 -0
- bead/simulation/noise_models/random_noise.py +82 -0
- bead/simulation/noise_models/systematic.py +132 -0
- bead/simulation/noise_models/temperature.py +86 -0
- bead/simulation/runner.py +144 -0
- bead/simulation/strategies/__init__.py +23 -0
- bead/simulation/strategies/base.py +123 -0
- bead/simulation/strategies/binary.py +103 -0
- bead/simulation/strategies/categorical.py +123 -0
- bead/simulation/strategies/cloze.py +224 -0
- bead/simulation/strategies/forced_choice.py +127 -0
- bead/simulation/strategies/free_text.py +105 -0
- bead/simulation/strategies/magnitude.py +116 -0
- bead/simulation/strategies/multi_select.py +129 -0
- bead/simulation/strategies/ordinal_scale.py +131 -0
- bead/templates/__init__.py +27 -0
- bead/templates/adapters/__init__.py +17 -0
- bead/templates/adapters/base.py +128 -0
- bead/templates/adapters/cache.py +178 -0
- bead/templates/adapters/huggingface.py +312 -0
- bead/templates/combinatorics.py +103 -0
- bead/templates/filler.py +605 -0
- bead/templates/renderers.py +177 -0
- bead/templates/resolver.py +178 -0
- bead/templates/strategies.py +1806 -0
- bead/templates/streaming.py +195 -0
- bead-0.1.0.dist-info/METADATA +212 -0
- bead-0.1.0.dist-info/RECORD +231 -0
- bead-0.1.0.dist-info/WHEEL +4 -0
- bead-0.1.0.dist-info/entry_points.txt +2 -0
- bead-0.1.0.dist-info/licenses/LICENSE +21 -0
bead/config/profiles.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Configuration profiles for the bead package.
|
|
2
|
+
|
|
3
|
+
This module provides pre-configured profiles for different environments
|
|
4
|
+
(development, production, testing) with optimized settings for each use case.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from tempfile import gettempdir
|
|
11
|
+
|
|
12
|
+
from bead.config.active_learning import (
|
|
13
|
+
ActiveLearningConfig,
|
|
14
|
+
ForcedChoiceModelConfig,
|
|
15
|
+
TrainerConfig,
|
|
16
|
+
)
|
|
17
|
+
from bead.config.config import BeadConfig
|
|
18
|
+
from bead.config.deployment import DeploymentConfig
|
|
19
|
+
from bead.config.item import ItemConfig
|
|
20
|
+
from bead.config.list import ListConfig
|
|
21
|
+
from bead.config.logging import LoggingConfig
|
|
22
|
+
from bead.config.model import ModelConfig
|
|
23
|
+
from bead.config.paths import PathsConfig
|
|
24
|
+
from bead.config.resources import ResourceConfig
|
|
25
|
+
from bead.config.template import TemplateConfig
|
|
26
|
+
|
|
27
|
+
# development profile: verbose logging, small batches, relative paths
|
|
28
|
+
DEV_CONFIG = BeadConfig(
|
|
29
|
+
profile="dev",
|
|
30
|
+
paths=PathsConfig(
|
|
31
|
+
data_dir=Path("data"),
|
|
32
|
+
output_dir=Path("output"),
|
|
33
|
+
cache_dir=Path(".cache"),
|
|
34
|
+
temp_dir=Path(gettempdir()) / "bead_dev",
|
|
35
|
+
create_dirs=True,
|
|
36
|
+
),
|
|
37
|
+
resources=ResourceConfig(
|
|
38
|
+
cache_external=False, # disable caching for development
|
|
39
|
+
),
|
|
40
|
+
templates=TemplateConfig(
|
|
41
|
+
filling_strategy="exhaustive",
|
|
42
|
+
batch_size=100, # small batch size for quick iteration
|
|
43
|
+
stream_mode=False,
|
|
44
|
+
),
|
|
45
|
+
items=ItemConfig(
|
|
46
|
+
model=ModelConfig(
|
|
47
|
+
provider="huggingface",
|
|
48
|
+
model_name="gpt2",
|
|
49
|
+
batch_size=4, # small batch for development
|
|
50
|
+
device="cpu",
|
|
51
|
+
),
|
|
52
|
+
parallel_processing=False, # simpler debugging without parallelism
|
|
53
|
+
),
|
|
54
|
+
lists=ListConfig(
|
|
55
|
+
num_lists=1,
|
|
56
|
+
),
|
|
57
|
+
deployment=DeploymentConfig(),
|
|
58
|
+
active_learning=ActiveLearningConfig(
|
|
59
|
+
forced_choice_model=ForcedChoiceModelConfig(
|
|
60
|
+
num_epochs=1, # quick training for testing
|
|
61
|
+
batch_size=8,
|
|
62
|
+
learning_rate=2e-5,
|
|
63
|
+
),
|
|
64
|
+
trainer=TrainerConfig(epochs=1),
|
|
65
|
+
),
|
|
66
|
+
logging=LoggingConfig(
|
|
67
|
+
level="DEBUG", # verbose logging for development
|
|
68
|
+
console=True,
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
"""Development configuration profile.
|
|
72
|
+
|
|
73
|
+
Optimized for:
|
|
74
|
+
- Quick iteration and debugging
|
|
75
|
+
- Verbose logging (DEBUG level)
|
|
76
|
+
- Small batch sizes for fast feedback
|
|
77
|
+
- No caching for fresh data
|
|
78
|
+
- Simple single-threaded processing
|
|
79
|
+
- Temporary directories for easy cleanup
|
|
80
|
+
|
|
81
|
+
Examples
|
|
82
|
+
--------
|
|
83
|
+
>>> from bead.config.profiles import DEV_CONFIG
|
|
84
|
+
>>> DEV_CONFIG.logging.level
|
|
85
|
+
'DEBUG'
|
|
86
|
+
>>> DEV_CONFIG.templates.batch_size
|
|
87
|
+
100
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# production profile: optimized settings, large batches, absolute paths
|
|
91
|
+
PROD_CONFIG = BeadConfig(
|
|
92
|
+
profile="prod",
|
|
93
|
+
paths=PathsConfig(
|
|
94
|
+
data_dir=Path("/var/bead/data").absolute(),
|
|
95
|
+
output_dir=Path("/var/bead/output").absolute(),
|
|
96
|
+
cache_dir=Path("/var/bead/cache").absolute(),
|
|
97
|
+
temp_dir=Path("/var/bead/temp").absolute(),
|
|
98
|
+
create_dirs=True,
|
|
99
|
+
),
|
|
100
|
+
resources=ResourceConfig(
|
|
101
|
+
cache_external=True, # enable caching for performance
|
|
102
|
+
),
|
|
103
|
+
templates=TemplateConfig(
|
|
104
|
+
filling_strategy="exhaustive",
|
|
105
|
+
batch_size=10000, # large batches for efficiency
|
|
106
|
+
stream_mode=True, # handle large templates efficiently
|
|
107
|
+
),
|
|
108
|
+
items=ItemConfig(
|
|
109
|
+
model=ModelConfig(
|
|
110
|
+
provider="huggingface",
|
|
111
|
+
model_name="gpt2",
|
|
112
|
+
batch_size=32, # large batch for production
|
|
113
|
+
device="cuda", # use GPU if available
|
|
114
|
+
),
|
|
115
|
+
parallel_processing=True, # enable parallelism
|
|
116
|
+
num_workers=8, # multiple workers
|
|
117
|
+
),
|
|
118
|
+
lists=ListConfig(
|
|
119
|
+
num_lists=1,
|
|
120
|
+
),
|
|
121
|
+
deployment=DeploymentConfig(
|
|
122
|
+
apply_material_design=True,
|
|
123
|
+
include_demographics=True,
|
|
124
|
+
include_attention_checks=True,
|
|
125
|
+
),
|
|
126
|
+
active_learning=ActiveLearningConfig(
|
|
127
|
+
forced_choice_model=ForcedChoiceModelConfig(
|
|
128
|
+
num_epochs=10, # full training
|
|
129
|
+
batch_size=32,
|
|
130
|
+
learning_rate=2e-5,
|
|
131
|
+
),
|
|
132
|
+
trainer=TrainerConfig(epochs=10, use_wandb=True),
|
|
133
|
+
),
|
|
134
|
+
logging=LoggingConfig(
|
|
135
|
+
level="WARNING", # minimal logging for production
|
|
136
|
+
console=False, # log to file only
|
|
137
|
+
file=Path("/var/log/bead/app.log"),
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
"""Production configuration profile.
|
|
141
|
+
|
|
142
|
+
Optimized for:
|
|
143
|
+
- Maximum performance and throughput
|
|
144
|
+
- Large batch sizes for efficiency
|
|
145
|
+
- GPU acceleration (when available)
|
|
146
|
+
- Parallel processing
|
|
147
|
+
- External caching enabled
|
|
148
|
+
- Minimal logging (WARNING level)
|
|
149
|
+
- Absolute paths to production directories
|
|
150
|
+
- Metrics tracking with W&B
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
>>> from bead.config.profiles import PROD_CONFIG
|
|
155
|
+
>>> PROD_CONFIG.logging.level
|
|
156
|
+
'WARNING'
|
|
157
|
+
>>> PROD_CONFIG.templates.batch_size
|
|
158
|
+
10000
|
|
159
|
+
>>> PROD_CONFIG.items.parallel_processing
|
|
160
|
+
True
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
# test profile: minimal logging, tiny batches, temp directories
|
|
164
|
+
TEST_CONFIG = BeadConfig(
|
|
165
|
+
profile="test",
|
|
166
|
+
paths=PathsConfig(
|
|
167
|
+
data_dir=Path(gettempdir()) / "bead_test" / "data",
|
|
168
|
+
output_dir=Path(gettempdir()) / "bead_test" / "output",
|
|
169
|
+
cache_dir=Path(gettempdir()) / "bead_test" / "cache",
|
|
170
|
+
temp_dir=Path(gettempdir()) / "bead_test" / "temp",
|
|
171
|
+
create_dirs=True,
|
|
172
|
+
),
|
|
173
|
+
resources=ResourceConfig(
|
|
174
|
+
cache_external=False, # no caching for tests
|
|
175
|
+
),
|
|
176
|
+
templates=TemplateConfig(
|
|
177
|
+
filling_strategy="exhaustive",
|
|
178
|
+
batch_size=10, # tiny batches for fast tests
|
|
179
|
+
max_combinations=100, # limit for tests
|
|
180
|
+
random_seed=42, # reproducible tests
|
|
181
|
+
),
|
|
182
|
+
items=ItemConfig(
|
|
183
|
+
model=ModelConfig(
|
|
184
|
+
provider="huggingface",
|
|
185
|
+
model_name="gpt2",
|
|
186
|
+
batch_size=1, # minimal batch for tests
|
|
187
|
+
device="cpu", # CPU only for tests
|
|
188
|
+
),
|
|
189
|
+
parallel_processing=False, # simpler test execution
|
|
190
|
+
num_workers=1,
|
|
191
|
+
),
|
|
192
|
+
lists=ListConfig(
|
|
193
|
+
num_lists=1,
|
|
194
|
+
random_seed=42, # reproducible tests
|
|
195
|
+
),
|
|
196
|
+
deployment=DeploymentConfig(
|
|
197
|
+
apply_material_design=False, # minimal for tests
|
|
198
|
+
include_demographics=False,
|
|
199
|
+
include_attention_checks=False,
|
|
200
|
+
),
|
|
201
|
+
active_learning=ActiveLearningConfig(
|
|
202
|
+
forced_choice_model=ForcedChoiceModelConfig(
|
|
203
|
+
num_epochs=1, # minimal training
|
|
204
|
+
batch_size=2,
|
|
205
|
+
learning_rate=2e-5,
|
|
206
|
+
),
|
|
207
|
+
trainer=TrainerConfig(epochs=1, use_wandb=False),
|
|
208
|
+
),
|
|
209
|
+
logging=LoggingConfig(
|
|
210
|
+
level="CRITICAL", # minimal logging for tests
|
|
211
|
+
console=False, # quiet tests
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
"""Test configuration profile.
|
|
215
|
+
|
|
216
|
+
Optimized for:
|
|
217
|
+
- Fast test execution
|
|
218
|
+
- Reproducibility (fixed random seeds)
|
|
219
|
+
- Minimal resource usage
|
|
220
|
+
- Tiny batch sizes
|
|
221
|
+
- Temporary directories for isolation
|
|
222
|
+
- Minimal logging (CRITICAL level)
|
|
223
|
+
- No external dependencies
|
|
224
|
+
- CPU-only execution
|
|
225
|
+
|
|
226
|
+
Examples
|
|
227
|
+
--------
|
|
228
|
+
>>> from bead.config.profiles import TEST_CONFIG
|
|
229
|
+
>>> TEST_CONFIG.logging.level
|
|
230
|
+
'CRITICAL'
|
|
231
|
+
>>> TEST_CONFIG.templates.batch_size
|
|
232
|
+
10
|
|
233
|
+
>>> TEST_CONFIG.templates.random_seed
|
|
234
|
+
42
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
# profile registry
|
|
238
|
+
PROFILES: dict[str, BeadConfig] = {
|
|
239
|
+
"default": BeadConfig(), # default from models
|
|
240
|
+
"dev": DEV_CONFIG,
|
|
241
|
+
"prod": PROD_CONFIG,
|
|
242
|
+
"test": TEST_CONFIG,
|
|
243
|
+
}
|
|
244
|
+
"""Registry of all available configuration profiles.
|
|
245
|
+
|
|
246
|
+
Maps profile names to their corresponding BeadConfig instances.
|
|
247
|
+
|
|
248
|
+
Examples
|
|
249
|
+
--------
|
|
250
|
+
>>> from bead.config.profiles import PROFILES
|
|
251
|
+
>>> list(PROFILES.keys())
|
|
252
|
+
['default', 'dev', 'prod', 'test']
|
|
253
|
+
>>> PROFILES["dev"].logging.level
|
|
254
|
+
'DEBUG'
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def get_profile(name: str) -> BeadConfig:
|
|
259
|
+
"""Get configuration profile by name.
|
|
260
|
+
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
name : str
|
|
264
|
+
Profile name. Must be one of: 'default', 'dev', 'prod', 'test'.
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
BeadConfig
|
|
269
|
+
Configuration for the specified profile.
|
|
270
|
+
|
|
271
|
+
Raises
|
|
272
|
+
------
|
|
273
|
+
ValueError
|
|
274
|
+
If profile name is not found in the registry.
|
|
275
|
+
|
|
276
|
+
Examples
|
|
277
|
+
--------
|
|
278
|
+
>>> from bead.config.profiles import get_profile
|
|
279
|
+
>>> config = get_profile("dev")
|
|
280
|
+
>>> config.profile
|
|
281
|
+
'dev'
|
|
282
|
+
>>> config.logging.level
|
|
283
|
+
'DEBUG'
|
|
284
|
+
|
|
285
|
+
>>> try:
|
|
286
|
+
... get_profile("invalid")
|
|
287
|
+
... except ValueError as e:
|
|
288
|
+
... print(str(e))
|
|
289
|
+
Profile 'invalid' not found. Available profiles: default, dev, prod, test
|
|
290
|
+
"""
|
|
291
|
+
if name not in PROFILES:
|
|
292
|
+
available = ", ".join(sorted(PROFILES.keys()))
|
|
293
|
+
msg = f"Profile {name!r} not found. Available profiles: {available}"
|
|
294
|
+
raise ValueError(msg)
|
|
295
|
+
|
|
296
|
+
return PROFILES[name].model_copy(deep=True)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def list_profiles() -> list[str]:
|
|
300
|
+
"""Return list of available profile names.
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
list[str]
|
|
305
|
+
List of available profile names, sorted alphabetically.
|
|
306
|
+
|
|
307
|
+
Examples
|
|
308
|
+
--------
|
|
309
|
+
>>> from bead.config.profiles import list_profiles
|
|
310
|
+
>>> profiles = list_profiles()
|
|
311
|
+
>>> "default" in profiles
|
|
312
|
+
True
|
|
313
|
+
>>> "dev" in profiles
|
|
314
|
+
True
|
|
315
|
+
>>> "prod" in profiles
|
|
316
|
+
True
|
|
317
|
+
>>> "test" in profiles
|
|
318
|
+
True
|
|
319
|
+
"""
|
|
320
|
+
return sorted(PROFILES.keys())
|
bead/config/resources.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Resource configuration models for the bead package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ResourceConfig(BaseModel):
|
|
11
|
+
"""Configuration for external resources.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
lexicon_path : Path | None
|
|
16
|
+
Path to lexicon file.
|
|
17
|
+
templates_path : Path | None
|
|
18
|
+
Path to templates file.
|
|
19
|
+
constraints_path : Path | None
|
|
20
|
+
Path to constraints file.
|
|
21
|
+
external_adapters : list[str]
|
|
22
|
+
List of external adapters to enable.
|
|
23
|
+
cache_external : bool
|
|
24
|
+
Whether to cache external resource lookups.
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
>>> config = ResourceConfig()
|
|
29
|
+
>>> config.cache_external
|
|
30
|
+
True
|
|
31
|
+
>>> config.external_adapters
|
|
32
|
+
[]
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
lexicon_path: Path | None = Field(default=None, description="Path to lexicon file")
|
|
36
|
+
templates_path: Path | None = Field(
|
|
37
|
+
default=None, description="Path to templates file"
|
|
38
|
+
)
|
|
39
|
+
constraints_path: Path | None = Field(
|
|
40
|
+
default=None, description="Path to constraints file"
|
|
41
|
+
)
|
|
42
|
+
external_adapters: list[str] = Field(
|
|
43
|
+
default_factory=list, description="External adapters to enable"
|
|
44
|
+
)
|
|
45
|
+
cache_external: bool = Field(
|
|
46
|
+
default=True, description="Cache external resource lookups"
|
|
47
|
+
)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Configuration serialization to YAML format.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for serializing BeadConfig objects to YAML format,
|
|
4
|
+
including conversion to dictionaries and saving to files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from bead.config.config import BeadConfig
|
|
13
|
+
from bead.config.defaults import get_default_config
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def config_to_dict(
|
|
17
|
+
config: BeadConfig, include_defaults: bool = False
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
"""Convert BeadConfig to dictionary for YAML serialization.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
config : BeadConfig
|
|
24
|
+
Configuration to convert.
|
|
25
|
+
include_defaults : bool
|
|
26
|
+
Whether to include default values.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
dict[str, Any]
|
|
31
|
+
Dictionary representation suitable for YAML.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
>>> from bead.config import get_default_config
|
|
36
|
+
>>> config = get_default_config()
|
|
37
|
+
>>> config_dict = config_to_dict(config)
|
|
38
|
+
>>> 'profile' in config_dict
|
|
39
|
+
True
|
|
40
|
+
"""
|
|
41
|
+
# get dictionary with all values, excluding unset fields
|
|
42
|
+
config_dict: dict[str, Any] = config.model_dump(
|
|
43
|
+
mode="json", exclude_unset=not include_defaults
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if not include_defaults:
|
|
47
|
+
# get default config to compare against
|
|
48
|
+
default_config = get_default_config()
|
|
49
|
+
default_dict: dict[str, Any] = default_config.model_dump(mode="json")
|
|
50
|
+
# remove values that match defaults
|
|
51
|
+
config_dict = _remove_defaults(config_dict, default_dict) # type: ignore[arg-type]
|
|
52
|
+
|
|
53
|
+
# convert Path objects to strings
|
|
54
|
+
config_dict = _convert_paths_to_strings(config_dict) # type: ignore[arg-type]
|
|
55
|
+
|
|
56
|
+
return config_dict
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _remove_defaults(
|
|
60
|
+
config_dict: dict[str, Any], default_dict: dict[str, Any]
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Remove values that match defaults from config dictionary.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
config_dict : dict[str, Any]
|
|
67
|
+
Configuration dictionary.
|
|
68
|
+
default_dict : dict[str, Any]
|
|
69
|
+
Default configuration dictionary.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
dict[str, Any]
|
|
74
|
+
Configuration dictionary with defaults removed.
|
|
75
|
+
"""
|
|
76
|
+
result: dict[str, Any] = {}
|
|
77
|
+
for key, value in config_dict.items():
|
|
78
|
+
if key not in default_dict:
|
|
79
|
+
# keep values not in defaults
|
|
80
|
+
result[key] = value
|
|
81
|
+
elif isinstance(value, dict) and isinstance(default_dict[key], dict):
|
|
82
|
+
# recursively remove defaults from nested dicts
|
|
83
|
+
nested_result = _remove_defaults(value, default_dict[key]) # type: ignore[arg-type]
|
|
84
|
+
if nested_result: # only include if not empty after removing defaults
|
|
85
|
+
result[key] = nested_result
|
|
86
|
+
elif value != default_dict[key]:
|
|
87
|
+
# keep values that differ from defaults
|
|
88
|
+
result[key] = value
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _convert_paths_to_strings(data: dict[str, Any]) -> dict[str, Any]:
|
|
93
|
+
"""Convert Path objects to strings in dictionary.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
data : dict[str, Any]
|
|
98
|
+
Dictionary potentially containing Path objects.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
dict[str, Any]
|
|
103
|
+
Dictionary with Path objects converted to strings.
|
|
104
|
+
"""
|
|
105
|
+
result: dict[str, Any] = {}
|
|
106
|
+
for key, value in data.items():
|
|
107
|
+
if isinstance(value, dict):
|
|
108
|
+
result[key] = _convert_paths_to_strings(value) # type: ignore[arg-type]
|
|
109
|
+
elif isinstance(value, Path):
|
|
110
|
+
result[key] = str(value)
|
|
111
|
+
elif isinstance(value, list):
|
|
112
|
+
converted_list: list[Any] = [
|
|
113
|
+
str(item) if isinstance(item, Path) else item
|
|
114
|
+
for item in value # type: ignore[misc]
|
|
115
|
+
]
|
|
116
|
+
result[key] = converted_list
|
|
117
|
+
else:
|
|
118
|
+
result[key] = value
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def to_yaml(config: BeadConfig, include_defaults: bool = False) -> str:
|
|
123
|
+
"""Serialize configuration to YAML string.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
config : BeadConfig
|
|
128
|
+
Configuration to serialize.
|
|
129
|
+
include_defaults : bool
|
|
130
|
+
If True, include all fields even if they have default values.
|
|
131
|
+
If False, only include non-default values.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
str
|
|
136
|
+
YAML representation of configuration.
|
|
137
|
+
|
|
138
|
+
Examples
|
|
139
|
+
--------
|
|
140
|
+
>>> from bead.config import get_default_config
|
|
141
|
+
>>> config = get_default_config()
|
|
142
|
+
>>> yaml_str = to_yaml(config)
|
|
143
|
+
>>> 'profile: default' in yaml_str
|
|
144
|
+
True
|
|
145
|
+
"""
|
|
146
|
+
config_dict = config_to_dict(config, include_defaults=include_defaults)
|
|
147
|
+
|
|
148
|
+
# configure YAML dumper for clean output
|
|
149
|
+
return yaml.dump(
|
|
150
|
+
config_dict,
|
|
151
|
+
default_flow_style=False,
|
|
152
|
+
sort_keys=True,
|
|
153
|
+
allow_unicode=True,
|
|
154
|
+
indent=2,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def save_yaml(
|
|
159
|
+
config: BeadConfig,
|
|
160
|
+
path: Path | str,
|
|
161
|
+
include_defaults: bool = False,
|
|
162
|
+
create_dirs: bool = True,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Save configuration to YAML file.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
config : BeadConfig
|
|
169
|
+
Configuration to save.
|
|
170
|
+
path : Path | str
|
|
171
|
+
Path where YAML file should be saved.
|
|
172
|
+
include_defaults : bool
|
|
173
|
+
If True, include all fields even if they have default values.
|
|
174
|
+
create_dirs : bool
|
|
175
|
+
If True, create parent directories if they don't exist.
|
|
176
|
+
|
|
177
|
+
Raises
|
|
178
|
+
------
|
|
179
|
+
IOError
|
|
180
|
+
If file cannot be written.
|
|
181
|
+
FileNotFoundError
|
|
182
|
+
If create_dirs is False and parent directory doesn't exist.
|
|
183
|
+
|
|
184
|
+
Examples
|
|
185
|
+
--------
|
|
186
|
+
>>> from pathlib import Path
|
|
187
|
+
>>> from bead.config import get_default_config
|
|
188
|
+
>>> config = get_default_config()
|
|
189
|
+
>>> save_yaml(config, Path("config.yaml"))
|
|
190
|
+
"""
|
|
191
|
+
path = Path(path) if isinstance(path, str) else path
|
|
192
|
+
|
|
193
|
+
# ensure parent directory exists
|
|
194
|
+
if create_dirs:
|
|
195
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
196
|
+
elif not path.parent.exists():
|
|
197
|
+
raise FileNotFoundError(
|
|
198
|
+
f"Parent directory does not exist: {path.parent}. "
|
|
199
|
+
f"Set create_dirs=True to create it automatically."
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# get YAML string
|
|
203
|
+
yaml_str = to_yaml(config, include_defaults=include_defaults)
|
|
204
|
+
|
|
205
|
+
# write to file
|
|
206
|
+
try:
|
|
207
|
+
with open(path, "w") as f:
|
|
208
|
+
f.write(yaml_str)
|
|
209
|
+
except OSError as e:
|
|
210
|
+
raise OSError(f"Failed to write YAML file {path}: {e}") from e
|