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/dsl/parser.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Constraint DSL parser.
|
|
2
|
+
|
|
3
|
+
This module provides the parser for constraint expressions using the Lark
|
|
4
|
+
parsing library. The parser converts constraint strings into AST nodes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from lark import Lark, Token, Transformer
|
|
12
|
+
from lark.exceptions import LarkError, UnexpectedCharacters, UnexpectedInput
|
|
13
|
+
|
|
14
|
+
from bead.dsl import ast
|
|
15
|
+
from bead.dsl.errors import ParseError
|
|
16
|
+
|
|
17
|
+
# Load grammar from file
|
|
18
|
+
_GRAMMAR_PATH = Path(__file__).parent / "grammar.lark"
|
|
19
|
+
_GRAMMAR = _GRAMMAR_PATH.read_text()
|
|
20
|
+
|
|
21
|
+
# Create Lark parser
|
|
22
|
+
_PARSER = Lark(
|
|
23
|
+
_GRAMMAR,
|
|
24
|
+
start="start",
|
|
25
|
+
parser="lalr", # Fast LALR parser
|
|
26
|
+
propagate_positions=True, # Track line/column info
|
|
27
|
+
keep_all_tokens=True, # Keep operator tokens
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ASTBuilder(Transformer): # type: ignore[type-arg]
|
|
32
|
+
"""Transformer that converts Lark parse tree to AST nodes."""
|
|
33
|
+
|
|
34
|
+
def string_literal(self, items: list[Token]) -> ast.Literal:
|
|
35
|
+
"""Transform string literal."""
|
|
36
|
+
token = items[0]
|
|
37
|
+
# Remove quotes
|
|
38
|
+
value = str(token.value)[1:-1]
|
|
39
|
+
return ast.Literal(value=value)
|
|
40
|
+
|
|
41
|
+
def number_literal(self, items: list[Token]) -> ast.Literal:
|
|
42
|
+
"""Transform number literal."""
|
|
43
|
+
token = items[0]
|
|
44
|
+
value_str = str(token.value)
|
|
45
|
+
# Parse as int or float
|
|
46
|
+
value: int | float = (
|
|
47
|
+
int(value_str) if "." not in value_str else float(value_str)
|
|
48
|
+
)
|
|
49
|
+
return ast.Literal(value=value)
|
|
50
|
+
|
|
51
|
+
def true_literal(self, items: list[Token]) -> ast.Literal:
|
|
52
|
+
"""Transform true literal."""
|
|
53
|
+
return ast.Literal(value=True)
|
|
54
|
+
|
|
55
|
+
def false_literal(self, items: list[Token]) -> ast.Literal:
|
|
56
|
+
"""Transform false literal."""
|
|
57
|
+
return ast.Literal(value=False)
|
|
58
|
+
|
|
59
|
+
def variable(self, items: list[Token]) -> ast.Variable:
|
|
60
|
+
"""Transform variable reference."""
|
|
61
|
+
name = str(items[0].value)
|
|
62
|
+
return ast.Variable(name=name)
|
|
63
|
+
|
|
64
|
+
def binary_op(self, items: list[Token | ast.ASTNode]) -> ast.BinaryOp:
|
|
65
|
+
"""Transform binary operation."""
|
|
66
|
+
# Items: [left, operator_token, right]
|
|
67
|
+
left = items[0]
|
|
68
|
+
# Get operator from token
|
|
69
|
+
operator_token = items[1]
|
|
70
|
+
if isinstance(operator_token, Token):
|
|
71
|
+
operator = str(operator_token.value)
|
|
72
|
+
else:
|
|
73
|
+
operator = str(operator_token)
|
|
74
|
+
right = items[2]
|
|
75
|
+
return ast.BinaryOp(operator=operator, left=left, right=right)
|
|
76
|
+
|
|
77
|
+
def binary_op_not_in(self, items: list[Token | ast.ASTNode]) -> ast.BinaryOp:
|
|
78
|
+
"""Transform 'not in' binary operation."""
|
|
79
|
+
# Items: [left, Token('not'), Token('in'), right]
|
|
80
|
+
# Filter to get only AST nodes
|
|
81
|
+
nodes = [item for item in items if not isinstance(item, Token)]
|
|
82
|
+
left = nodes[0]
|
|
83
|
+
right = nodes[1]
|
|
84
|
+
return ast.BinaryOp(operator="not in", left=left, right=right)
|
|
85
|
+
|
|
86
|
+
def unary_op(self, items: list[Token | ast.ASTNode]) -> ast.UnaryOp:
|
|
87
|
+
"""Transform unary operation."""
|
|
88
|
+
# Items: [operator_token, operand]
|
|
89
|
+
operator_token = items[0]
|
|
90
|
+
if isinstance(operator_token, Token):
|
|
91
|
+
operator = str(operator_token.value)
|
|
92
|
+
else:
|
|
93
|
+
operator = str(operator_token)
|
|
94
|
+
operand = items[1]
|
|
95
|
+
return ast.UnaryOp(operator=operator, operand=operand)
|
|
96
|
+
|
|
97
|
+
def attribute_access(self, items: list[Token | ast.ASTNode]) -> ast.AttributeAccess:
|
|
98
|
+
"""Transform attribute access."""
|
|
99
|
+
# Items: [object, dot_token, name_token]
|
|
100
|
+
obj = items[0]
|
|
101
|
+
# Last token is the attribute name
|
|
102
|
+
attribute = str(items[-1].value)
|
|
103
|
+
return ast.AttributeAccess(object=obj, attribute=attribute)
|
|
104
|
+
|
|
105
|
+
def subscript(self, items: list[Token | ast.ASTNode]) -> ast.Subscript:
|
|
106
|
+
"""Transform subscript access."""
|
|
107
|
+
# Items: [object, lbracket_token, index_expr, rbracket_token]
|
|
108
|
+
obj = items[0]
|
|
109
|
+
# The index expression is items[1] (non-Token items)
|
|
110
|
+
index_expr = [item for item in items[1:] if not isinstance(item, Token)][0]
|
|
111
|
+
return ast.Subscript(object=obj, index=index_expr)
|
|
112
|
+
|
|
113
|
+
def function_call(
|
|
114
|
+
self, items: list[Token | ast.ASTNode | list[ast.ASTNode] | None]
|
|
115
|
+
) -> ast.FunctionCall:
|
|
116
|
+
"""Transform function call."""
|
|
117
|
+
# Items: [atom, lparen, arguments_list/None, rparen]
|
|
118
|
+
# The first item is the function expression (can be Variable or AttributeAccess)
|
|
119
|
+
if not items or not isinstance(items[0], ast.ASTNode):
|
|
120
|
+
raise ValueError("Function call must have a function expression")
|
|
121
|
+
function: ast.ASTNode = items[0]
|
|
122
|
+
|
|
123
|
+
# Arguments are in the list returned by the arguments rule (if present)
|
|
124
|
+
# Filter for non-Token, non-None items and flatten lists
|
|
125
|
+
arguments: list[ast.ASTNode] = []
|
|
126
|
+
for item in items[1:]:
|
|
127
|
+
if isinstance(item, list):
|
|
128
|
+
# This is the arguments list - arg is already ast.ASTNode
|
|
129
|
+
for arg in item:
|
|
130
|
+
arguments.append(arg)
|
|
131
|
+
elif isinstance(item, ast.ASTNode):
|
|
132
|
+
arguments.append(item)
|
|
133
|
+
return ast.FunctionCall(function=function, arguments=arguments)
|
|
134
|
+
|
|
135
|
+
def list_literal(
|
|
136
|
+
self, items: list[Token | ast.ASTNode | list[ast.ASTNode] | None]
|
|
137
|
+
) -> ast.ListLiteral:
|
|
138
|
+
"""Transform list literal."""
|
|
139
|
+
# Filter out bracket tokens and None, flatten lists
|
|
140
|
+
elements: list[ast.ASTNode] = []
|
|
141
|
+
for item in items:
|
|
142
|
+
if isinstance(item, list):
|
|
143
|
+
# This is the list_elements list - elem is already ast.ASTNode
|
|
144
|
+
for elem in item:
|
|
145
|
+
elements.append(elem)
|
|
146
|
+
elif isinstance(item, ast.ASTNode):
|
|
147
|
+
elements.append(item)
|
|
148
|
+
return ast.ListLiteral(elements=elements)
|
|
149
|
+
|
|
150
|
+
def arguments(self, items: list[Token | ast.ASTNode]) -> list[ast.ASTNode]:
|
|
151
|
+
"""Transform function arguments, returning flat list."""
|
|
152
|
+
# Filter out comma tokens
|
|
153
|
+
return [item for item in items if isinstance(item, ast.ASTNode)]
|
|
154
|
+
|
|
155
|
+
def list_elements(self, items: list[Token | ast.ASTNode]) -> list[ast.ASTNode]:
|
|
156
|
+
"""Transform list elements, returning flat list."""
|
|
157
|
+
# Filter out comma tokens
|
|
158
|
+
return [item for item in items if isinstance(item, ast.ASTNode)]
|
|
159
|
+
|
|
160
|
+
def atom(self, items: list[Token | ast.ASTNode]) -> ast.ASTNode:
|
|
161
|
+
"""Transform atom (handles parenthesized expressions)."""
|
|
162
|
+
# If we have parentheses, items = [lparen_token, expr, rparen_token]
|
|
163
|
+
# Return just the expression (filter out tokens)
|
|
164
|
+
nodes = [item for item in items if isinstance(item, ast.ASTNode)]
|
|
165
|
+
if nodes:
|
|
166
|
+
return nodes[0]
|
|
167
|
+
# Fallback: if no AST nodes, try to get first item (shouldn't happen)
|
|
168
|
+
if items and isinstance(items[0], ast.ASTNode):
|
|
169
|
+
return items[0]
|
|
170
|
+
raise ValueError("Atom must contain an AST node")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def parse(expression: str) -> ast.ASTNode:
|
|
174
|
+
"""Parse constraint expression into AST.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
expression : str
|
|
179
|
+
Constraint expression to parse.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
ast.ASTNode
|
|
184
|
+
Root node of the abstract syntax tree.
|
|
185
|
+
|
|
186
|
+
Raises
|
|
187
|
+
------
|
|
188
|
+
ParseError
|
|
189
|
+
If the expression cannot be parsed.
|
|
190
|
+
|
|
191
|
+
Examples
|
|
192
|
+
--------
|
|
193
|
+
>>> node = parse("pos == 'VERB'")
|
|
194
|
+
>>> isinstance(node, ast.BinaryOp)
|
|
195
|
+
True
|
|
196
|
+
>>> node.operator
|
|
197
|
+
'=='
|
|
198
|
+
"""
|
|
199
|
+
try:
|
|
200
|
+
# Parse with Lark
|
|
201
|
+
tree = _PARSER.parse(expression) # type: ignore[no-untyped-call]
|
|
202
|
+
|
|
203
|
+
# Transform to AST
|
|
204
|
+
transformer = ASTBuilder()
|
|
205
|
+
result = transformer.transform(tree)
|
|
206
|
+
|
|
207
|
+
if not isinstance(result, ast.ASTNode):
|
|
208
|
+
raise ParseError(
|
|
209
|
+
f"Parser returned unexpected type: {type(result)}", text=expression
|
|
210
|
+
)
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
except UnexpectedCharacters as e:
|
|
214
|
+
# Handle invalid characters (must come before UnexpectedInput)
|
|
215
|
+
raise ParseError(
|
|
216
|
+
f"Unexpected character at position {e.column}",
|
|
217
|
+
line=e.line,
|
|
218
|
+
column=e.column,
|
|
219
|
+
text=expression,
|
|
220
|
+
) from e
|
|
221
|
+
except UnexpectedInput as e:
|
|
222
|
+
# Handle UnexpectedToken (has .token) and UnexpectedEOF
|
|
223
|
+
token_str = getattr(e, "token", "unexpected input")
|
|
224
|
+
raise ParseError(
|
|
225
|
+
f"Unexpected input: {token_str}",
|
|
226
|
+
line=e.line,
|
|
227
|
+
column=e.column,
|
|
228
|
+
text=expression,
|
|
229
|
+
) from e
|
|
230
|
+
except LarkError as e:
|
|
231
|
+
raise ParseError(f"Parse error: {e}", text=expression) from e
|