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/stdlib.py
ADDED
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
"""Standard library functions for constraint DSL.
|
|
2
|
+
|
|
3
|
+
This module provides built-in functions that can be used in constraint
|
|
4
|
+
expressions. Functions are organized by category and registered with
|
|
5
|
+
the evaluation context.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
import random
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from bead.dsl.context import EvaluationContext
|
|
17
|
+
from bead.items.item import Item
|
|
18
|
+
|
|
19
|
+
# Type for DSL scalar values that can be compared/processed
|
|
20
|
+
DslScalar = str | int | float | bool | None
|
|
21
|
+
|
|
22
|
+
# Type for collections in DSL
|
|
23
|
+
DslCollection = list[DslScalar] | dict[str, DslScalar]
|
|
24
|
+
|
|
25
|
+
# Generic type for min/max/any/all operations
|
|
26
|
+
T = TypeVar("T")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# String functions
|
|
30
|
+
def len_(
|
|
31
|
+
s: str
|
|
32
|
+
| list[str | int | float | bool | None]
|
|
33
|
+
| dict[str, str | int | float | bool | None]
|
|
34
|
+
| tuple[str | int | float | bool | None, ...],
|
|
35
|
+
) -> int:
|
|
36
|
+
"""Return length of string or collection.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
s : str | list | dict | tuple
|
|
41
|
+
String or collection to measure.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
int
|
|
46
|
+
Length of string or collection.
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
>>> len_("hello")
|
|
51
|
+
5
|
|
52
|
+
>>> len_([1, 2, 3])
|
|
53
|
+
3
|
|
54
|
+
"""
|
|
55
|
+
return len(s)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def lower(s: str) -> str:
|
|
59
|
+
"""Convert string to lowercase.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
s : str
|
|
64
|
+
String to convert.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
str
|
|
69
|
+
Lowercase string.
|
|
70
|
+
|
|
71
|
+
Examples
|
|
72
|
+
--------
|
|
73
|
+
>>> lower("HELLO")
|
|
74
|
+
'hello'
|
|
75
|
+
"""
|
|
76
|
+
return s.lower()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def upper(s: str) -> str:
|
|
80
|
+
"""Convert string to uppercase.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
s : str
|
|
85
|
+
String to convert.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
str
|
|
90
|
+
Uppercase string.
|
|
91
|
+
|
|
92
|
+
Examples
|
|
93
|
+
--------
|
|
94
|
+
>>> upper("hello")
|
|
95
|
+
'HELLO'
|
|
96
|
+
"""
|
|
97
|
+
return s.upper()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def startswith(s: str, prefix: str) -> bool:
|
|
101
|
+
"""Check if string starts with prefix.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
s : str
|
|
106
|
+
String to check.
|
|
107
|
+
prefix : str
|
|
108
|
+
Prefix to look for.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
bool
|
|
113
|
+
True if string starts with prefix.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> startswith("hello", "hel")
|
|
118
|
+
True
|
|
119
|
+
>>> startswith("hello", "bye")
|
|
120
|
+
False
|
|
121
|
+
"""
|
|
122
|
+
return s.startswith(prefix)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def endswith(s: str, suffix: str) -> bool:
|
|
126
|
+
"""Check if string ends with suffix.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
s : str
|
|
131
|
+
String to check.
|
|
132
|
+
suffix : str
|
|
133
|
+
Suffix to look for.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
bool
|
|
138
|
+
True if string ends with suffix.
|
|
139
|
+
|
|
140
|
+
Examples
|
|
141
|
+
--------
|
|
142
|
+
>>> endswith("hello", "lo")
|
|
143
|
+
True
|
|
144
|
+
>>> endswith("hello", "hi")
|
|
145
|
+
False
|
|
146
|
+
"""
|
|
147
|
+
return s.endswith(suffix)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def contains(s: str, substring: str) -> bool:
|
|
151
|
+
"""Check if string contains substring.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
s : str
|
|
156
|
+
String to check.
|
|
157
|
+
substring : str
|
|
158
|
+
Substring to look for.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
bool
|
|
163
|
+
True if string contains substring.
|
|
164
|
+
|
|
165
|
+
Examples
|
|
166
|
+
--------
|
|
167
|
+
>>> contains("hello", "ell")
|
|
168
|
+
True
|
|
169
|
+
>>> contains("hello", "bye")
|
|
170
|
+
False
|
|
171
|
+
"""
|
|
172
|
+
return substring in s
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def replace(s: str, old: str, new: str) -> str:
|
|
176
|
+
"""Replace occurrences of substring.
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
s : str
|
|
181
|
+
String to modify.
|
|
182
|
+
old : str
|
|
183
|
+
Substring to replace.
|
|
184
|
+
new : str
|
|
185
|
+
Replacement substring.
|
|
186
|
+
|
|
187
|
+
Returns
|
|
188
|
+
-------
|
|
189
|
+
str
|
|
190
|
+
String with replacements.
|
|
191
|
+
|
|
192
|
+
Examples
|
|
193
|
+
--------
|
|
194
|
+
>>> replace("hello world", "world", "there")
|
|
195
|
+
'hello there'
|
|
196
|
+
"""
|
|
197
|
+
return s.replace(old, new)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def split(s: str, sep: str = " ") -> list[str]:
|
|
201
|
+
"""Split string by separator.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
s : str
|
|
206
|
+
String to split.
|
|
207
|
+
sep : str
|
|
208
|
+
Separator string. Defaults to space.
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
list[str]
|
|
213
|
+
List of substrings.
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
>>> split("a,b,c", ",")
|
|
218
|
+
['a', 'b', 'c']
|
|
219
|
+
"""
|
|
220
|
+
return s.split(sep)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# Collection functions
|
|
224
|
+
def count(collection: str | list[DslScalar], item: DslScalar) -> int:
|
|
225
|
+
"""Count occurrences of item in collection.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
collection : str | list[DslScalar]
|
|
230
|
+
Collection to search.
|
|
231
|
+
item : DslScalar
|
|
232
|
+
Item to count.
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
int
|
|
237
|
+
Number of occurrences.
|
|
238
|
+
|
|
239
|
+
Examples
|
|
240
|
+
--------
|
|
241
|
+
>>> count([1, 2, 2, 3], 2)
|
|
242
|
+
2
|
|
243
|
+
>>> count("hello", "l")
|
|
244
|
+
2
|
|
245
|
+
"""
|
|
246
|
+
return collection.count(item)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def sum_(collection: list[int | float]) -> int | float:
|
|
250
|
+
"""Sum numeric collection.
|
|
251
|
+
|
|
252
|
+
Parameters
|
|
253
|
+
----------
|
|
254
|
+
collection : list[int | float]
|
|
255
|
+
Collection of numbers.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
int | float
|
|
260
|
+
Sum of all numbers.
|
|
261
|
+
|
|
262
|
+
Examples
|
|
263
|
+
--------
|
|
264
|
+
>>> sum_([1, 2, 3])
|
|
265
|
+
6
|
|
266
|
+
>>> sum_([1.5, 2.5])
|
|
267
|
+
4.0
|
|
268
|
+
"""
|
|
269
|
+
return sum(collection)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def min_(collection: list[DslScalar]) -> DslScalar:
|
|
273
|
+
"""Return minimum value from collection.
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
collection : list[DslScalar]
|
|
278
|
+
Collection to search.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
DslScalar
|
|
283
|
+
Minimum value.
|
|
284
|
+
|
|
285
|
+
Examples
|
|
286
|
+
--------
|
|
287
|
+
>>> min_([3, 1, 2])
|
|
288
|
+
1
|
|
289
|
+
"""
|
|
290
|
+
return min(collection)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def max_[T](collection: list[T]) -> T:
|
|
294
|
+
"""Return maximum value from collection.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
collection : list[T]
|
|
299
|
+
Collection to search.
|
|
300
|
+
|
|
301
|
+
Returns
|
|
302
|
+
-------
|
|
303
|
+
T
|
|
304
|
+
Maximum value.
|
|
305
|
+
|
|
306
|
+
Examples
|
|
307
|
+
--------
|
|
308
|
+
>>> max_([3, 1, 2])
|
|
309
|
+
3
|
|
310
|
+
"""
|
|
311
|
+
return max(collection)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def any_[T](collection: list[T]) -> bool:
|
|
315
|
+
"""Check if any element is truthy.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
collection : list[T]
|
|
320
|
+
Collection to check.
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
bool
|
|
325
|
+
True if any element is truthy.
|
|
326
|
+
|
|
327
|
+
Examples
|
|
328
|
+
--------
|
|
329
|
+
>>> any_([False, True, False])
|
|
330
|
+
True
|
|
331
|
+
>>> any_([False, False])
|
|
332
|
+
False
|
|
333
|
+
"""
|
|
334
|
+
return any(collection)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def all_[T](collection: list[T]) -> bool:
|
|
338
|
+
"""Check if all elements are truthy.
|
|
339
|
+
|
|
340
|
+
Parameters
|
|
341
|
+
----------
|
|
342
|
+
collection : list[T]
|
|
343
|
+
Collection to check.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
bool
|
|
348
|
+
True if all elements are truthy.
|
|
349
|
+
|
|
350
|
+
Examples
|
|
351
|
+
--------
|
|
352
|
+
>>> all_([True, True, True])
|
|
353
|
+
True
|
|
354
|
+
>>> all_([True, False, True])
|
|
355
|
+
False
|
|
356
|
+
"""
|
|
357
|
+
return all(collection)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
# Type checking functions
|
|
361
|
+
def is_str(value: DslScalar) -> bool:
|
|
362
|
+
"""Check if value is a string.
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
value : DslScalar
|
|
367
|
+
Value to check.
|
|
368
|
+
|
|
369
|
+
Returns
|
|
370
|
+
-------
|
|
371
|
+
bool
|
|
372
|
+
True if value is a string.
|
|
373
|
+
|
|
374
|
+
Examples
|
|
375
|
+
--------
|
|
376
|
+
>>> is_str("hello")
|
|
377
|
+
True
|
|
378
|
+
>>> is_str(42)
|
|
379
|
+
False
|
|
380
|
+
"""
|
|
381
|
+
return isinstance(value, str)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def is_int(value: DslScalar) -> bool:
|
|
385
|
+
"""Check if value is an integer.
|
|
386
|
+
|
|
387
|
+
Parameters
|
|
388
|
+
----------
|
|
389
|
+
value : DslScalar
|
|
390
|
+
Value to check.
|
|
391
|
+
|
|
392
|
+
Returns
|
|
393
|
+
-------
|
|
394
|
+
bool
|
|
395
|
+
True if value is an integer.
|
|
396
|
+
|
|
397
|
+
Examples
|
|
398
|
+
--------
|
|
399
|
+
>>> is_int(42)
|
|
400
|
+
True
|
|
401
|
+
>>> is_int(42.0)
|
|
402
|
+
False
|
|
403
|
+
"""
|
|
404
|
+
return isinstance(value, int) and not isinstance(value, bool)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def is_float(value: DslScalar) -> bool:
|
|
408
|
+
"""Check if value is a float.
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
value : DslScalar
|
|
413
|
+
Value to check.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
bool
|
|
418
|
+
True if value is a float.
|
|
419
|
+
|
|
420
|
+
Examples
|
|
421
|
+
--------
|
|
422
|
+
>>> is_float(42.0)
|
|
423
|
+
True
|
|
424
|
+
>>> is_float(42)
|
|
425
|
+
False
|
|
426
|
+
"""
|
|
427
|
+
return isinstance(value, float)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def is_bool(value: DslScalar) -> bool:
|
|
431
|
+
"""Check if value is a boolean.
|
|
432
|
+
|
|
433
|
+
Parameters
|
|
434
|
+
----------
|
|
435
|
+
value : DslScalar
|
|
436
|
+
Value to check.
|
|
437
|
+
|
|
438
|
+
Returns
|
|
439
|
+
-------
|
|
440
|
+
bool
|
|
441
|
+
True if value is a boolean.
|
|
442
|
+
|
|
443
|
+
Examples
|
|
444
|
+
--------
|
|
445
|
+
>>> is_bool(True)
|
|
446
|
+
True
|
|
447
|
+
>>> is_bool(1)
|
|
448
|
+
False
|
|
449
|
+
"""
|
|
450
|
+
return isinstance(value, bool)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def is_list(value: DslScalar | list[DslScalar]) -> bool:
|
|
454
|
+
"""Check if value is a list.
|
|
455
|
+
|
|
456
|
+
Parameters
|
|
457
|
+
----------
|
|
458
|
+
value : DslScalar | list[DslScalar]
|
|
459
|
+
Value to check.
|
|
460
|
+
|
|
461
|
+
Returns
|
|
462
|
+
-------
|
|
463
|
+
bool
|
|
464
|
+
True if value is a list.
|
|
465
|
+
|
|
466
|
+
Examples
|
|
467
|
+
--------
|
|
468
|
+
>>> is_list([1, 2, 3])
|
|
469
|
+
True
|
|
470
|
+
>>> is_list((1, 2, 3))
|
|
471
|
+
False
|
|
472
|
+
"""
|
|
473
|
+
return isinstance(value, list)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# Conversion functions
|
|
477
|
+
def str_(value: DslScalar) -> str:
|
|
478
|
+
"""Convert value to string.
|
|
479
|
+
|
|
480
|
+
Parameters
|
|
481
|
+
----------
|
|
482
|
+
value : DslScalar
|
|
483
|
+
Value to convert.
|
|
484
|
+
|
|
485
|
+
Returns
|
|
486
|
+
-------
|
|
487
|
+
str
|
|
488
|
+
String representation of value.
|
|
489
|
+
|
|
490
|
+
Examples
|
|
491
|
+
--------
|
|
492
|
+
>>> str_(42)
|
|
493
|
+
'42'
|
|
494
|
+
>>> str_(True)
|
|
495
|
+
'True'
|
|
496
|
+
"""
|
|
497
|
+
return str(value)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
# Math functions
|
|
501
|
+
def abs_(value: int | float) -> int | float:
|
|
502
|
+
"""Return absolute value.
|
|
503
|
+
|
|
504
|
+
Parameters
|
|
505
|
+
----------
|
|
506
|
+
value : int | float
|
|
507
|
+
Numeric value.
|
|
508
|
+
|
|
509
|
+
Returns
|
|
510
|
+
-------
|
|
511
|
+
int | float
|
|
512
|
+
Absolute value.
|
|
513
|
+
|
|
514
|
+
Examples
|
|
515
|
+
--------
|
|
516
|
+
>>> abs_(-5)
|
|
517
|
+
5
|
|
518
|
+
>>> abs_(5)
|
|
519
|
+
5
|
|
520
|
+
"""
|
|
521
|
+
return abs(value)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def round_(value: float, ndigits: int = 0) -> float:
|
|
525
|
+
"""Round numeric value.
|
|
526
|
+
|
|
527
|
+
Parameters
|
|
528
|
+
----------
|
|
529
|
+
value : float
|
|
530
|
+
Value to round.
|
|
531
|
+
ndigits : int
|
|
532
|
+
Number of decimal places.
|
|
533
|
+
|
|
534
|
+
Returns
|
|
535
|
+
-------
|
|
536
|
+
float
|
|
537
|
+
Rounded value.
|
|
538
|
+
|
|
539
|
+
Examples
|
|
540
|
+
--------
|
|
541
|
+
>>> round_(3.14159, 2)
|
|
542
|
+
3.14
|
|
543
|
+
"""
|
|
544
|
+
return round(value, ndigits)
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def floor(value: float) -> int:
|
|
548
|
+
"""Return floor of value.
|
|
549
|
+
|
|
550
|
+
Parameters
|
|
551
|
+
----------
|
|
552
|
+
value : float
|
|
553
|
+
Numeric value.
|
|
554
|
+
|
|
555
|
+
Returns
|
|
556
|
+
-------
|
|
557
|
+
int
|
|
558
|
+
Floor value.
|
|
559
|
+
|
|
560
|
+
Examples
|
|
561
|
+
--------
|
|
562
|
+
>>> floor(3.7)
|
|
563
|
+
3
|
|
564
|
+
>>> floor(-3.7)
|
|
565
|
+
-4
|
|
566
|
+
"""
|
|
567
|
+
return math.floor(value)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def ceil(value: float) -> int:
|
|
571
|
+
"""Return ceiling of value.
|
|
572
|
+
|
|
573
|
+
Parameters
|
|
574
|
+
----------
|
|
575
|
+
value : float
|
|
576
|
+
Numeric value.
|
|
577
|
+
|
|
578
|
+
Returns
|
|
579
|
+
-------
|
|
580
|
+
int
|
|
581
|
+
Ceiling value.
|
|
582
|
+
|
|
583
|
+
Examples
|
|
584
|
+
--------
|
|
585
|
+
>>> ceil(3.2)
|
|
586
|
+
4
|
|
587
|
+
>>> ceil(-3.2)
|
|
588
|
+
-3
|
|
589
|
+
"""
|
|
590
|
+
return math.ceil(value)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
# Logic functions
|
|
594
|
+
def not_(value: DslScalar | list[DslScalar]) -> bool:
|
|
595
|
+
"""Return logical negation of value.
|
|
596
|
+
|
|
597
|
+
Parameters
|
|
598
|
+
----------
|
|
599
|
+
value : DslScalar | list[DslScalar]
|
|
600
|
+
Value to negate.
|
|
601
|
+
|
|
602
|
+
Returns
|
|
603
|
+
-------
|
|
604
|
+
bool
|
|
605
|
+
Logical negation.
|
|
606
|
+
|
|
607
|
+
Examples
|
|
608
|
+
--------
|
|
609
|
+
>>> not_(True)
|
|
610
|
+
False
|
|
611
|
+
>>> not_(False)
|
|
612
|
+
True
|
|
613
|
+
>>> not_(0)
|
|
614
|
+
True
|
|
615
|
+
"""
|
|
616
|
+
return not value
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
# ============================================================================
|
|
620
|
+
# Simulation Functions
|
|
621
|
+
# ============================================================================
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def sigmoid(x: float) -> float:
|
|
625
|
+
"""Sigmoid activation function.
|
|
626
|
+
|
|
627
|
+
Converts unbounded value to probability in (0, 1).
|
|
628
|
+
|
|
629
|
+
Parameters
|
|
630
|
+
----------
|
|
631
|
+
x : float
|
|
632
|
+
Input value.
|
|
633
|
+
|
|
634
|
+
Returns
|
|
635
|
+
-------
|
|
636
|
+
float
|
|
637
|
+
Sigmoid output in (0, 1).
|
|
638
|
+
|
|
639
|
+
Examples
|
|
640
|
+
--------
|
|
641
|
+
>>> sigmoid(0.0)
|
|
642
|
+
0.5
|
|
643
|
+
>>> round(sigmoid(5.0), 3)
|
|
644
|
+
0.993
|
|
645
|
+
>>> round(sigmoid(-5.0), 3)
|
|
646
|
+
0.007
|
|
647
|
+
"""
|
|
648
|
+
return 1.0 / (1.0 + math.exp(-x))
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def softmax(values: list[float]) -> list[float]:
|
|
652
|
+
"""Softmax function over list of values.
|
|
653
|
+
|
|
654
|
+
Converts list of scores to probability distribution.
|
|
655
|
+
|
|
656
|
+
Parameters
|
|
657
|
+
----------
|
|
658
|
+
values : list[float]
|
|
659
|
+
Input scores.
|
|
660
|
+
|
|
661
|
+
Returns
|
|
662
|
+
-------
|
|
663
|
+
list[float]
|
|
664
|
+
Probability distribution (sums to 1.0).
|
|
665
|
+
|
|
666
|
+
Examples
|
|
667
|
+
--------
|
|
668
|
+
>>> probs = softmax([1.0, 2.0, 3.0])
|
|
669
|
+
>>> [round(p, 2) for p in probs]
|
|
670
|
+
[0.09, 0.24, 0.67]
|
|
671
|
+
"""
|
|
672
|
+
if not values:
|
|
673
|
+
return []
|
|
674
|
+
exp_values = [math.exp(v) for v in values]
|
|
675
|
+
total = sum(exp_values)
|
|
676
|
+
return [e / total for e in exp_values]
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def sample_categorical(probs: list[float], seed: int | None = None) -> int:
|
|
680
|
+
"""Sample from categorical distribution.
|
|
681
|
+
|
|
682
|
+
Parameters
|
|
683
|
+
----------
|
|
684
|
+
probs : list[float]
|
|
685
|
+
Probability distribution.
|
|
686
|
+
seed : int | None
|
|
687
|
+
Random seed.
|
|
688
|
+
|
|
689
|
+
Returns
|
|
690
|
+
-------
|
|
691
|
+
int
|
|
692
|
+
Sampled index (0-based).
|
|
693
|
+
|
|
694
|
+
Examples
|
|
695
|
+
--------
|
|
696
|
+
>>> sample_categorical([0.2, 0.5, 0.3], seed=42)
|
|
697
|
+
1
|
|
698
|
+
"""
|
|
699
|
+
if seed is not None:
|
|
700
|
+
random.seed(seed)
|
|
701
|
+
return random.choices(range(len(probs)), weights=probs)[0]
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def add_noise(
|
|
705
|
+
value: float, noise_type: str, strength: float, seed: int | None = None
|
|
706
|
+
) -> float:
|
|
707
|
+
"""Add noise to a value.
|
|
708
|
+
|
|
709
|
+
Parameters
|
|
710
|
+
----------
|
|
711
|
+
value : float
|
|
712
|
+
Original value.
|
|
713
|
+
noise_type : str
|
|
714
|
+
Type of noise ("gaussian", "uniform").
|
|
715
|
+
strength : float
|
|
716
|
+
Noise strength (stddev for gaussian, range for uniform).
|
|
717
|
+
seed : int | None
|
|
718
|
+
Random seed.
|
|
719
|
+
|
|
720
|
+
Returns
|
|
721
|
+
-------
|
|
722
|
+
float
|
|
723
|
+
Value with noise added.
|
|
724
|
+
|
|
725
|
+
Examples
|
|
726
|
+
--------
|
|
727
|
+
>>> result = add_noise(5.0, "gaussian", 0.1, seed=42)
|
|
728
|
+
>>> isinstance(result, float)
|
|
729
|
+
True
|
|
730
|
+
>>> result = add_noise(5.0, "uniform", 0.1, seed=42)
|
|
731
|
+
>>> isinstance(result, float)
|
|
732
|
+
True
|
|
733
|
+
"""
|
|
734
|
+
if seed is not None:
|
|
735
|
+
random.seed(seed)
|
|
736
|
+
|
|
737
|
+
if noise_type == "gaussian":
|
|
738
|
+
return value + random.gauss(0, strength)
|
|
739
|
+
elif noise_type == "uniform":
|
|
740
|
+
return value + random.uniform(-strength, strength)
|
|
741
|
+
else:
|
|
742
|
+
return value
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def model_output(
|
|
746
|
+
item: Item, key: str, default: DslScalar = None
|
|
747
|
+
) -> DslScalar | list[float]:
|
|
748
|
+
"""Extract model output from item.
|
|
749
|
+
|
|
750
|
+
Parameters
|
|
751
|
+
----------
|
|
752
|
+
item : Item
|
|
753
|
+
Item with model outputs.
|
|
754
|
+
key : str
|
|
755
|
+
Key to extract (e.g., "lm_score", "embedding").
|
|
756
|
+
default : DslScalar
|
|
757
|
+
Default value if key not found.
|
|
758
|
+
|
|
759
|
+
Returns
|
|
760
|
+
-------
|
|
761
|
+
DslScalar | list[float]
|
|
762
|
+
Extracted value or default.
|
|
763
|
+
|
|
764
|
+
Examples
|
|
765
|
+
--------
|
|
766
|
+
>>> # Would work with actual Item object
|
|
767
|
+
>>> # model_output(item, "lm_score", default=0.0)
|
|
768
|
+
>>> # -12.4
|
|
769
|
+
"""
|
|
770
|
+
if not hasattr(item, "model_outputs"):
|
|
771
|
+
return default
|
|
772
|
+
|
|
773
|
+
for output in item.model_outputs:
|
|
774
|
+
if output.operation == key or key in output.computation_metadata:
|
|
775
|
+
return output.output
|
|
776
|
+
|
|
777
|
+
# Try item_metadata as fallback
|
|
778
|
+
if hasattr(item, "item_metadata") and key in item.item_metadata:
|
|
779
|
+
return item.item_metadata[key]
|
|
780
|
+
|
|
781
|
+
return default
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
def distance(emb1: list[float], emb2: list[float], metric: str = "cosine") -> float:
|
|
785
|
+
"""Compute distance between embeddings.
|
|
786
|
+
|
|
787
|
+
Parameters
|
|
788
|
+
----------
|
|
789
|
+
emb1 : list[float]
|
|
790
|
+
First embedding.
|
|
791
|
+
emb2 : list[float]
|
|
792
|
+
Second embedding.
|
|
793
|
+
metric : str
|
|
794
|
+
Distance metric ("cosine", "euclidean", "manhattan").
|
|
795
|
+
|
|
796
|
+
Returns
|
|
797
|
+
-------
|
|
798
|
+
float
|
|
799
|
+
Distance value.
|
|
800
|
+
|
|
801
|
+
Examples
|
|
802
|
+
--------
|
|
803
|
+
>>> distance([1.0, 0.0], [0.0, 1.0], "cosine")
|
|
804
|
+
1.0
|
|
805
|
+
>>> round(distance([1.0, 0.0], [0.0, 1.0], "euclidean"), 3)
|
|
806
|
+
1.414
|
|
807
|
+
>>> distance([1.0, 0.0], [0.0, 1.0], "manhattan")
|
|
808
|
+
2.0
|
|
809
|
+
"""
|
|
810
|
+
if metric == "cosine":
|
|
811
|
+
dot = sum(a * b for a, b in zip(emb1, emb2, strict=True))
|
|
812
|
+
norm1 = math.sqrt(sum(a * a for a in emb1))
|
|
813
|
+
norm2 = math.sqrt(sum(b * b for b in emb2))
|
|
814
|
+
if norm1 == 0 or norm2 == 0:
|
|
815
|
+
return 1.0
|
|
816
|
+
return 1.0 - (dot / (norm1 * norm2))
|
|
817
|
+
|
|
818
|
+
elif metric == "euclidean":
|
|
819
|
+
return math.sqrt(sum((a - b) ** 2 for a, b in zip(emb1, emb2, strict=True)))
|
|
820
|
+
|
|
821
|
+
elif metric == "manhattan":
|
|
822
|
+
return sum(abs(a - b) for a, b in zip(emb1, emb2, strict=True))
|
|
823
|
+
|
|
824
|
+
else:
|
|
825
|
+
msg = f"Unknown metric: {metric}"
|
|
826
|
+
raise ValueError(msg)
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def preference_prob(score1: float, score2: float, temperature: float = 1.0) -> float:
|
|
830
|
+
"""Compute preference probability using sigmoid.
|
|
831
|
+
|
|
832
|
+
P(choose option 1) = sigmoid((score1 - score2) / temperature)
|
|
833
|
+
|
|
834
|
+
Parameters
|
|
835
|
+
----------
|
|
836
|
+
score1 : float
|
|
837
|
+
Score for option 1.
|
|
838
|
+
score2 : float
|
|
839
|
+
Score for option 2.
|
|
840
|
+
temperature : float
|
|
841
|
+
Temperature for scaling.
|
|
842
|
+
|
|
843
|
+
Returns
|
|
844
|
+
-------
|
|
845
|
+
float
|
|
846
|
+
Probability of choosing option 1.
|
|
847
|
+
|
|
848
|
+
Examples
|
|
849
|
+
--------
|
|
850
|
+
>>> round(preference_prob(10.0, 5.0, temperature=1.0), 3)
|
|
851
|
+
0.993
|
|
852
|
+
>>> round(preference_prob(10.0, 5.0, temperature=5.0), 2)
|
|
853
|
+
0.73
|
|
854
|
+
"""
|
|
855
|
+
return sigmoid((score1 - score2) / temperature)
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
# Type alias for DSL callable functions
|
|
859
|
+
DslFunction = Callable[..., DslScalar | list[DslScalar] | list[float]]
|
|
860
|
+
|
|
861
|
+
# Register simulation functions
|
|
862
|
+
SIMULATION_FUNCTIONS: dict[str, DslFunction] = {
|
|
863
|
+
"sigmoid": sigmoid,
|
|
864
|
+
"softmax": softmax,
|
|
865
|
+
"sample_categorical": sample_categorical,
|
|
866
|
+
"add_noise": add_noise,
|
|
867
|
+
"model_output": model_output,
|
|
868
|
+
"distance": distance,
|
|
869
|
+
"preference_prob": preference_prob,
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
# Registry
|
|
874
|
+
STDLIB_FUNCTIONS: dict[str, DslFunction] = {
|
|
875
|
+
# String functions
|
|
876
|
+
"len": len_,
|
|
877
|
+
"lower": lower,
|
|
878
|
+
"upper": upper,
|
|
879
|
+
"startswith": startswith,
|
|
880
|
+
"endswith": endswith,
|
|
881
|
+
"contains": contains,
|
|
882
|
+
"replace": replace,
|
|
883
|
+
"split": split,
|
|
884
|
+
# Collection functions
|
|
885
|
+
"count": count,
|
|
886
|
+
"sum": sum_,
|
|
887
|
+
"min": min_,
|
|
888
|
+
"max": max_,
|
|
889
|
+
"any": any_,
|
|
890
|
+
"all": all_,
|
|
891
|
+
# Type checking
|
|
892
|
+
"is_str": is_str,
|
|
893
|
+
"is_int": is_int,
|
|
894
|
+
"is_float": is_float,
|
|
895
|
+
"is_bool": is_bool,
|
|
896
|
+
"is_list": is_list,
|
|
897
|
+
# Conversion functions
|
|
898
|
+
"str": str_,
|
|
899
|
+
# Math functions
|
|
900
|
+
"abs": abs_,
|
|
901
|
+
"round": round_,
|
|
902
|
+
"floor": floor,
|
|
903
|
+
"ceil": ceil,
|
|
904
|
+
# Logic functions
|
|
905
|
+
"not": not_,
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
# Update STDLIB_FUNCTIONS with simulation functions
|
|
909
|
+
STDLIB_FUNCTIONS.update(SIMULATION_FUNCTIONS)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def register_stdlib(context: EvaluationContext) -> None:
|
|
913
|
+
"""Register all standard library functions in context.
|
|
914
|
+
|
|
915
|
+
Parameters
|
|
916
|
+
----------
|
|
917
|
+
context : EvaluationContext
|
|
918
|
+
Context to register functions in.
|
|
919
|
+
|
|
920
|
+
Examples
|
|
921
|
+
--------
|
|
922
|
+
>>> from bead.dsl.context import EvaluationContext
|
|
923
|
+
>>> ctx = EvaluationContext()
|
|
924
|
+
>>> register_stdlib(ctx)
|
|
925
|
+
>>> ctx.call_function("len", ["hello"])
|
|
926
|
+
5
|
|
927
|
+
"""
|
|
928
|
+
for name, func in STDLIB_FUNCTIONS.items():
|
|
929
|
+
context.set_function(name, func)
|