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/cli/config.py
ADDED
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
"""Configuration commands for bead CLI.
|
|
2
|
+
|
|
3
|
+
This module provides commands for viewing, validating, and managing configuration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
import yaml
|
|
13
|
+
from pydantic import ValidationError
|
|
14
|
+
|
|
15
|
+
from bead.cli.utils import (
|
|
16
|
+
format_output,
|
|
17
|
+
get_nested_value,
|
|
18
|
+
load_config_for_cli,
|
|
19
|
+
merge_config_dicts,
|
|
20
|
+
print_error,
|
|
21
|
+
print_info,
|
|
22
|
+
print_success,
|
|
23
|
+
redact_sensitive_values,
|
|
24
|
+
)
|
|
25
|
+
from bead.config import list_profiles, validate_config
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.group()
|
|
29
|
+
def config() -> None:
|
|
30
|
+
r"""Manage configuration commands.
|
|
31
|
+
|
|
32
|
+
Provides commands for viewing, validating, and exporting configuration.
|
|
33
|
+
|
|
34
|
+
\b
|
|
35
|
+
Examples:
|
|
36
|
+
$ bead config show
|
|
37
|
+
$ bead config show --format json
|
|
38
|
+
$ bead config show --key paths.data_dir
|
|
39
|
+
$ bead config validate
|
|
40
|
+
$ bead config export --output my-config.yaml
|
|
41
|
+
$ bead config profiles
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@config.command()
|
|
46
|
+
@click.option(
|
|
47
|
+
"--format",
|
|
48
|
+
"-f",
|
|
49
|
+
"format_type",
|
|
50
|
+
type=click.Choice(["yaml", "json", "table"], case_sensitive=False),
|
|
51
|
+
default="yaml",
|
|
52
|
+
help="Output format (default: yaml)",
|
|
53
|
+
)
|
|
54
|
+
@click.option(
|
|
55
|
+
"--key",
|
|
56
|
+
"-k",
|
|
57
|
+
type=str,
|
|
58
|
+
default=None,
|
|
59
|
+
help="Show specific config value (e.g., paths.data_dir)",
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--no-redact",
|
|
63
|
+
is_flag=True,
|
|
64
|
+
default=False,
|
|
65
|
+
help="Show sensitive values (API keys, etc.)",
|
|
66
|
+
)
|
|
67
|
+
@click.pass_context
|
|
68
|
+
def show(
|
|
69
|
+
ctx: click.Context,
|
|
70
|
+
format_type: str,
|
|
71
|
+
key: str | None,
|
|
72
|
+
no_redact: bool,
|
|
73
|
+
) -> None:
|
|
74
|
+
r"""Display current configuration.
|
|
75
|
+
|
|
76
|
+
Shows the merged configuration from profile, file, and environment variables.
|
|
77
|
+
|
|
78
|
+
\b
|
|
79
|
+
Examples:
|
|
80
|
+
$ bead config show
|
|
81
|
+
$ bead config show --format json
|
|
82
|
+
$ bead config show --key paths.data_dir
|
|
83
|
+
$ bead config show --no-redact # Show API keys
|
|
84
|
+
"""
|
|
85
|
+
config_file = ctx.obj.get("config_file")
|
|
86
|
+
profile = ctx.obj.get("profile", "default")
|
|
87
|
+
verbose = ctx.obj.get("verbose", False)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
cfg = load_config_for_cli(
|
|
91
|
+
config_file=str(config_file) if config_file else None,
|
|
92
|
+
profile=profile,
|
|
93
|
+
verbose=verbose,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Convert to dict
|
|
97
|
+
config_dict = cfg.model_dump()
|
|
98
|
+
|
|
99
|
+
# Redact sensitive values unless --no-redact
|
|
100
|
+
if not no_redact:
|
|
101
|
+
config_dict = redact_sensitive_values(config_dict)
|
|
102
|
+
|
|
103
|
+
# Show specific key if requested
|
|
104
|
+
if key:
|
|
105
|
+
try:
|
|
106
|
+
value = get_nested_value(config_dict, key)
|
|
107
|
+
click.echo(value)
|
|
108
|
+
except KeyError as e:
|
|
109
|
+
print_error(f"Configuration key not found: {e}")
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# Format and display
|
|
113
|
+
try:
|
|
114
|
+
output = format_output(config_dict, format_type) # type: ignore[arg-type]
|
|
115
|
+
click.echo(output)
|
|
116
|
+
except ValueError as e:
|
|
117
|
+
print_error(f"Failed to format output: {e}")
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print_error(f"Failed to load configuration: {e}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@config.command()
|
|
124
|
+
@click.option(
|
|
125
|
+
"--config-file",
|
|
126
|
+
"-c",
|
|
127
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
128
|
+
default=None,
|
|
129
|
+
help="Configuration file to validate",
|
|
130
|
+
)
|
|
131
|
+
@click.pass_context
|
|
132
|
+
def validate(ctx: click.Context, config_file: Path | None) -> None:
|
|
133
|
+
r"""Validate configuration file.
|
|
134
|
+
|
|
135
|
+
Checks YAML syntax and validates against bead configuration schema.
|
|
136
|
+
|
|
137
|
+
\b
|
|
138
|
+
Examples:
|
|
139
|
+
$ bead config validate
|
|
140
|
+
$ bead config validate --config-file my-config.yaml
|
|
141
|
+
|
|
142
|
+
\b
|
|
143
|
+
Exit codes:
|
|
144
|
+
0 - Configuration is valid
|
|
145
|
+
1 - Configuration is invalid
|
|
146
|
+
"""
|
|
147
|
+
# Use CLI context config-file if not explicitly provided
|
|
148
|
+
if config_file is None:
|
|
149
|
+
config_file = ctx.obj.get("config_file")
|
|
150
|
+
|
|
151
|
+
if config_file is None:
|
|
152
|
+
print_error("No configuration file specified. Use --config-file or -c.")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
profile = ctx.obj.get("profile", "default")
|
|
156
|
+
verbose = ctx.obj.get("verbose", False)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
# Load and validate
|
|
160
|
+
cfg = load_config_for_cli(
|
|
161
|
+
config_file=str(config_file),
|
|
162
|
+
profile=profile,
|
|
163
|
+
verbose=verbose,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Additional validation
|
|
167
|
+
errors = validate_config(cfg)
|
|
168
|
+
|
|
169
|
+
if errors:
|
|
170
|
+
print_error("Configuration validation failed:")
|
|
171
|
+
for error in errors:
|
|
172
|
+
click.echo(f" • {error}", err=True)
|
|
173
|
+
click.get_current_context().exit(1)
|
|
174
|
+
else:
|
|
175
|
+
print_success(f"Configuration is valid: {config_file}")
|
|
176
|
+
|
|
177
|
+
except ValidationError as e:
|
|
178
|
+
print_error("Configuration validation failed:")
|
|
179
|
+
for error in e.errors():
|
|
180
|
+
location = " → ".join(str(loc) for loc in error["loc"])
|
|
181
|
+
click.echo(f" • {location}: {error['msg']}", err=True)
|
|
182
|
+
click.get_current_context().exit(1)
|
|
183
|
+
|
|
184
|
+
except Exception as e:
|
|
185
|
+
print_error(f"Failed to validate configuration: {e}")
|
|
186
|
+
click.get_current_context().exit(1)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@config.command()
|
|
190
|
+
@click.option(
|
|
191
|
+
"--output",
|
|
192
|
+
"-o",
|
|
193
|
+
type=click.Path(dir_okay=False, path_type=Path),
|
|
194
|
+
default=None,
|
|
195
|
+
help="Output file (default: stdout)",
|
|
196
|
+
)
|
|
197
|
+
@click.option(
|
|
198
|
+
"--comments",
|
|
199
|
+
is_flag=True,
|
|
200
|
+
default=False,
|
|
201
|
+
help="Include comments explaining each field",
|
|
202
|
+
)
|
|
203
|
+
@click.option(
|
|
204
|
+
"--no-redact",
|
|
205
|
+
is_flag=True,
|
|
206
|
+
default=False,
|
|
207
|
+
help="Include sensitive values (API keys, etc.)",
|
|
208
|
+
)
|
|
209
|
+
@click.pass_context
|
|
210
|
+
def export(
|
|
211
|
+
ctx: click.Context,
|
|
212
|
+
output: Path | None,
|
|
213
|
+
comments: bool,
|
|
214
|
+
no_redact: bool,
|
|
215
|
+
) -> None:
|
|
216
|
+
r"""Export current configuration to YAML.
|
|
217
|
+
|
|
218
|
+
Exports the merged configuration (profile + file + env) to a YAML file.
|
|
219
|
+
|
|
220
|
+
\b
|
|
221
|
+
Examples:
|
|
222
|
+
$ bead config export
|
|
223
|
+
$ bead config export --output my-config.yaml
|
|
224
|
+
$ bead config export --comments # Include field explanations
|
|
225
|
+
$ bead config export --no-redact --output full-config.yaml
|
|
226
|
+
"""
|
|
227
|
+
config_file = ctx.obj.get("config_file")
|
|
228
|
+
profile = ctx.obj.get("profile", "default")
|
|
229
|
+
verbose = ctx.obj.get("verbose", False)
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
cfg = load_config_for_cli(
|
|
233
|
+
config_file=str(config_file) if config_file else None,
|
|
234
|
+
profile=profile,
|
|
235
|
+
verbose=verbose,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Convert to dict
|
|
239
|
+
config_dict = cfg.model_dump()
|
|
240
|
+
|
|
241
|
+
# Redact sensitive values unless --no-redact
|
|
242
|
+
if not no_redact:
|
|
243
|
+
config_dict = redact_sensitive_values(config_dict)
|
|
244
|
+
|
|
245
|
+
# Add comments if requested
|
|
246
|
+
yaml_content = _generate_yaml_with_comments(config_dict) if comments else None
|
|
247
|
+
|
|
248
|
+
# Save or print
|
|
249
|
+
if output:
|
|
250
|
+
if yaml_content:
|
|
251
|
+
output.write_text(yaml_content)
|
|
252
|
+
else:
|
|
253
|
+
# Write config dict directly to YAML file
|
|
254
|
+
with open(output, "w") as f:
|
|
255
|
+
yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
|
|
256
|
+
print_success(f"Configuration exported to: {output}")
|
|
257
|
+
else:
|
|
258
|
+
if yaml_content:
|
|
259
|
+
click.echo(yaml_content)
|
|
260
|
+
else:
|
|
261
|
+
click.echo(
|
|
262
|
+
yaml.dump(config_dict, default_flow_style=False, sort_keys=False)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
print_error(f"Failed to export configuration: {e}")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@config.command()
|
|
270
|
+
def profiles() -> None:
|
|
271
|
+
r"""List available configuration profiles.
|
|
272
|
+
|
|
273
|
+
Shows all built-in profiles with descriptions.
|
|
274
|
+
|
|
275
|
+
\b
|
|
276
|
+
Examples:
|
|
277
|
+
$ bead config profiles
|
|
278
|
+
"""
|
|
279
|
+
available_profiles = list_profiles()
|
|
280
|
+
|
|
281
|
+
print_info("Available configuration profiles:")
|
|
282
|
+
click.echo()
|
|
283
|
+
|
|
284
|
+
for profile_name in available_profiles:
|
|
285
|
+
click.echo(f" • {profile_name}")
|
|
286
|
+
|
|
287
|
+
click.echo()
|
|
288
|
+
print_info("Use --profile to select a profile:")
|
|
289
|
+
click.echo(" $ bead --profile dev config show")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _generate_yaml_with_comments(config_dict: dict[str, Any]) -> str:
|
|
293
|
+
"""Generate YAML with comments explaining fields.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
config_dict : dict[str, Any]
|
|
298
|
+
Configuration dictionary.
|
|
299
|
+
|
|
300
|
+
Returns
|
|
301
|
+
-------
|
|
302
|
+
str
|
|
303
|
+
YAML content with comments.
|
|
304
|
+
"""
|
|
305
|
+
lines = ["# bead Configuration", "# Generated with comments", ""]
|
|
306
|
+
|
|
307
|
+
# Add commented sections
|
|
308
|
+
sections = {
|
|
309
|
+
"profile": "Configuration profile (default, dev, prod, test)",
|
|
310
|
+
"logging": "Logging configuration (level, format, file)",
|
|
311
|
+
"paths": "Path configuration (directories for data, models, cache)",
|
|
312
|
+
"resources": "Resource management (auto-download, caching, language)",
|
|
313
|
+
"templates": "Template filling (strategy, constraints, MLM settings)",
|
|
314
|
+
"models": "Model configuration (default models, GPU, API keys)",
|
|
315
|
+
"items": "Item construction (validation, auto-save)",
|
|
316
|
+
"lists": "List construction (partitioning, balancing)",
|
|
317
|
+
"deployment": "Deployment configuration (platform, jsPsych, plugins)",
|
|
318
|
+
"training": "Training configuration (framework, hyperparameters)",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for section, description in sections.items():
|
|
322
|
+
if section in config_dict:
|
|
323
|
+
lines.append(f"# {description}")
|
|
324
|
+
section_yaml = yaml.dump(
|
|
325
|
+
{section: config_dict[section]},
|
|
326
|
+
default_flow_style=False,
|
|
327
|
+
sort_keys=False,
|
|
328
|
+
)
|
|
329
|
+
lines.append(section_yaml.rstrip())
|
|
330
|
+
lines.append("")
|
|
331
|
+
|
|
332
|
+
return "\n".join(lines)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@config.command()
|
|
336
|
+
@click.option(
|
|
337
|
+
"--output",
|
|
338
|
+
"-o",
|
|
339
|
+
type=click.Path(dir_okay=False, path_type=Path),
|
|
340
|
+
required=True,
|
|
341
|
+
help="Output configuration file path",
|
|
342
|
+
)
|
|
343
|
+
@click.option(
|
|
344
|
+
"--selection-strategy",
|
|
345
|
+
type=click.Choice(["uncertainty", "diversity", "hybrid"], case_sensitive=False),
|
|
346
|
+
default="uncertainty",
|
|
347
|
+
help="Selection strategy for active learning",
|
|
348
|
+
)
|
|
349
|
+
@click.option(
|
|
350
|
+
"--budget",
|
|
351
|
+
type=int,
|
|
352
|
+
default=1000,
|
|
353
|
+
help="Annotation budget (default: 1000)",
|
|
354
|
+
)
|
|
355
|
+
@click.option(
|
|
356
|
+
"--convergence-threshold",
|
|
357
|
+
type=float,
|
|
358
|
+
default=0.85,
|
|
359
|
+
help="Convergence threshold (default: 0.85)",
|
|
360
|
+
)
|
|
361
|
+
@click.option(
|
|
362
|
+
"--checkpoint-interval",
|
|
363
|
+
type=int,
|
|
364
|
+
default=100,
|
|
365
|
+
help="Checkpoint interval (default: 100)",
|
|
366
|
+
)
|
|
367
|
+
def create_active_learning(
|
|
368
|
+
output: Path,
|
|
369
|
+
selection_strategy: str,
|
|
370
|
+
budget: int,
|
|
371
|
+
convergence_threshold: float,
|
|
372
|
+
checkpoint_interval: int,
|
|
373
|
+
) -> None:
|
|
374
|
+
r"""Create active learning configuration file.
|
|
375
|
+
|
|
376
|
+
Examples
|
|
377
|
+
--------
|
|
378
|
+
$ bead config create-active-learning --output al_config.yaml
|
|
379
|
+
$ bead config create-active-learning --output al_config.yaml \
|
|
380
|
+
--selection-strategy hybrid --budget 2000
|
|
381
|
+
"""
|
|
382
|
+
config_dict = {
|
|
383
|
+
"active_learning": {
|
|
384
|
+
"selection_strategy": selection_strategy,
|
|
385
|
+
"budget": budget,
|
|
386
|
+
"convergence_threshold": convergence_threshold,
|
|
387
|
+
"checkpoint_interval": checkpoint_interval,
|
|
388
|
+
"min_iterations": 10,
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
with open(output, "w") as f:
|
|
393
|
+
yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
|
|
394
|
+
|
|
395
|
+
print_success(f"Created active learning configuration: {output}")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@config.command()
|
|
399
|
+
@click.option(
|
|
400
|
+
"--output",
|
|
401
|
+
"-o",
|
|
402
|
+
type=click.Path(dir_okay=False, path_type=Path),
|
|
403
|
+
required=True,
|
|
404
|
+
help="Output configuration file path",
|
|
405
|
+
)
|
|
406
|
+
@click.option(
|
|
407
|
+
"--task-type",
|
|
408
|
+
type=click.Choice(
|
|
409
|
+
[
|
|
410
|
+
"forced_choice",
|
|
411
|
+
"ordinal_scale",
|
|
412
|
+
"categorical",
|
|
413
|
+
"binary",
|
|
414
|
+
"multi_select",
|
|
415
|
+
"magnitude",
|
|
416
|
+
"free_text",
|
|
417
|
+
"cloze",
|
|
418
|
+
],
|
|
419
|
+
case_sensitive=False,
|
|
420
|
+
),
|
|
421
|
+
required=True,
|
|
422
|
+
help="Task type for model",
|
|
423
|
+
)
|
|
424
|
+
@click.option(
|
|
425
|
+
"--base-model",
|
|
426
|
+
type=str,
|
|
427
|
+
default="bert-base-uncased",
|
|
428
|
+
help="Base model name (default: bert-base-uncased)",
|
|
429
|
+
)
|
|
430
|
+
@click.option(
|
|
431
|
+
"--mixed-effects-mode",
|
|
432
|
+
type=click.Choice(
|
|
433
|
+
["fixed-only", "random-intercepts", "random-slopes"],
|
|
434
|
+
case_sensitive=False,
|
|
435
|
+
),
|
|
436
|
+
default="fixed-only",
|
|
437
|
+
help="Mixed effects mode (default: fixed-only)",
|
|
438
|
+
)
|
|
439
|
+
@click.option(
|
|
440
|
+
"--use-lora",
|
|
441
|
+
is_flag=True,
|
|
442
|
+
help="Use LoRA parameter-efficient fine-tuning",
|
|
443
|
+
)
|
|
444
|
+
def create_model(
|
|
445
|
+
output: Path,
|
|
446
|
+
task_type: str,
|
|
447
|
+
base_model: str,
|
|
448
|
+
mixed_effects_mode: str,
|
|
449
|
+
use_lora: bool,
|
|
450
|
+
) -> None:
|
|
451
|
+
r"""Create model configuration file.
|
|
452
|
+
|
|
453
|
+
Examples
|
|
454
|
+
--------
|
|
455
|
+
$ bead config create-model --output model_config.yaml \
|
|
456
|
+
--task-type forced_choice
|
|
457
|
+
$ bead config create-model --output model_config.yaml \
|
|
458
|
+
--task-type ordinal_scale --mixed-effects-mode random-intercepts
|
|
459
|
+
"""
|
|
460
|
+
config_dict: dict[str, Any] = {
|
|
461
|
+
"model": {
|
|
462
|
+
"task_type": task_type,
|
|
463
|
+
"base_model": base_model,
|
|
464
|
+
"mixed_effects_mode": mixed_effects_mode,
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if use_lora:
|
|
469
|
+
config_dict["model"]["lora"] = {
|
|
470
|
+
"enabled": True,
|
|
471
|
+
"rank": 8,
|
|
472
|
+
"alpha": 16,
|
|
473
|
+
"dropout": 0.1,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if mixed_effects_mode in ("random-intercepts", "random-slopes"):
|
|
477
|
+
config_dict["model"]["mixed_effects_config"] = {
|
|
478
|
+
"participant_intercept": True,
|
|
479
|
+
"item_intercept": True,
|
|
480
|
+
"interaction": mixed_effects_mode == "random-slopes",
|
|
481
|
+
"variance_components": {
|
|
482
|
+
"participant_variance": 1.0,
|
|
483
|
+
"item_variance": 1.0,
|
|
484
|
+
"interaction_variance": (
|
|
485
|
+
0.5 if mixed_effects_mode == "random-slopes" else 0.0
|
|
486
|
+
),
|
|
487
|
+
},
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
with open(output, "w") as f:
|
|
491
|
+
yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
|
|
492
|
+
|
|
493
|
+
print_success(f"Created model configuration: {output}")
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
@config.command()
|
|
497
|
+
@click.option(
|
|
498
|
+
"--output",
|
|
499
|
+
"-o",
|
|
500
|
+
type=click.Path(dir_okay=False, path_type=Path),
|
|
501
|
+
required=True,
|
|
502
|
+
help="Output configuration file path",
|
|
503
|
+
)
|
|
504
|
+
@click.option(
|
|
505
|
+
"--annotator-type",
|
|
506
|
+
type=click.Choice(
|
|
507
|
+
["oracle", "random", "lm-based", "distance-based"],
|
|
508
|
+
case_sensitive=False,
|
|
509
|
+
),
|
|
510
|
+
default="oracle",
|
|
511
|
+
help="Annotator type (default: oracle)",
|
|
512
|
+
)
|
|
513
|
+
@click.option(
|
|
514
|
+
"--model-name",
|
|
515
|
+
type=str,
|
|
516
|
+
help="Model name for lm-based annotator",
|
|
517
|
+
)
|
|
518
|
+
@click.option(
|
|
519
|
+
"--noise-model",
|
|
520
|
+
type=click.Choice(["random", "systematic", "temperature"], case_sensitive=False),
|
|
521
|
+
help="Noise model type",
|
|
522
|
+
)
|
|
523
|
+
@click.option(
|
|
524
|
+
"--noise-level",
|
|
525
|
+
type=float,
|
|
526
|
+
default=0.05,
|
|
527
|
+
help="Noise level (default: 0.05)",
|
|
528
|
+
)
|
|
529
|
+
@click.option(
|
|
530
|
+
"--n-annotators",
|
|
531
|
+
type=int,
|
|
532
|
+
default=20,
|
|
533
|
+
help="Number of simulated annotators (default: 20)",
|
|
534
|
+
)
|
|
535
|
+
def create_simulation(
|
|
536
|
+
output: Path,
|
|
537
|
+
annotator_type: str,
|
|
538
|
+
model_name: str | None,
|
|
539
|
+
noise_model: str | None,
|
|
540
|
+
noise_level: float,
|
|
541
|
+
n_annotators: int,
|
|
542
|
+
) -> None:
|
|
543
|
+
r"""Create simulation configuration file.
|
|
544
|
+
|
|
545
|
+
Examples
|
|
546
|
+
--------
|
|
547
|
+
$ bead config create-simulation --output sim_config.yaml
|
|
548
|
+
$ bead config create-simulation --output sim_config.yaml \
|
|
549
|
+
--annotator-type lm-based --model-name gpt-4
|
|
550
|
+
$ bead config create-simulation --output sim_config.yaml \
|
|
551
|
+
--noise-model random --noise-level 0.1
|
|
552
|
+
"""
|
|
553
|
+
config_dict: dict[str, Any] = {
|
|
554
|
+
"simulation": {
|
|
555
|
+
"annotator_type": annotator_type,
|
|
556
|
+
"n_annotators": n_annotators,
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if annotator_type == "lm-based" and model_name:
|
|
561
|
+
config_dict["simulation"]["model_name"] = model_name
|
|
562
|
+
|
|
563
|
+
if noise_model:
|
|
564
|
+
config_dict["simulation"]["noise_model"] = {
|
|
565
|
+
"type": noise_model,
|
|
566
|
+
"level": noise_level,
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
with open(output, "w") as f:
|
|
570
|
+
yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
|
|
571
|
+
|
|
572
|
+
print_success(f"Created simulation configuration: {output}")
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@config.command()
|
|
576
|
+
@click.option(
|
|
577
|
+
"--base",
|
|
578
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
579
|
+
required=True,
|
|
580
|
+
help="Base configuration file",
|
|
581
|
+
)
|
|
582
|
+
@click.option(
|
|
583
|
+
"--override",
|
|
584
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
585
|
+
required=True,
|
|
586
|
+
help="Override configuration file",
|
|
587
|
+
)
|
|
588
|
+
@click.option(
|
|
589
|
+
"--output",
|
|
590
|
+
"-o",
|
|
591
|
+
type=click.Path(dir_okay=False, path_type=Path),
|
|
592
|
+
required=True,
|
|
593
|
+
help="Output merged configuration file",
|
|
594
|
+
)
|
|
595
|
+
def merge(
|
|
596
|
+
base: Path,
|
|
597
|
+
override: Path,
|
|
598
|
+
output: Path,
|
|
599
|
+
) -> None:
|
|
600
|
+
"""Merge two configuration files.
|
|
601
|
+
|
|
602
|
+
Examples
|
|
603
|
+
--------
|
|
604
|
+
$ bead config merge --base base.yaml --override custom.yaml --output merged.yaml
|
|
605
|
+
"""
|
|
606
|
+
try:
|
|
607
|
+
# Load both files
|
|
608
|
+
with open(base) as f:
|
|
609
|
+
base_config = yaml.safe_load(f)
|
|
610
|
+
|
|
611
|
+
with open(override) as f:
|
|
612
|
+
override_config = yaml.safe_load(f)
|
|
613
|
+
|
|
614
|
+
# Merge recursively
|
|
615
|
+
merged = merge_config_dicts(base_config, override_config)
|
|
616
|
+
|
|
617
|
+
# Write merged config
|
|
618
|
+
with open(output, "w") as f:
|
|
619
|
+
yaml.dump(merged, f, default_flow_style=False, sort_keys=False)
|
|
620
|
+
|
|
621
|
+
print_success(f"Merged configurations: {output}")
|
|
622
|
+
|
|
623
|
+
except Exception as e:
|
|
624
|
+
print_error(f"Failed to merge configurations: {e}")
|