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.
Files changed (231) hide show
  1. bead/__init__.py +11 -0
  2. bead/__main__.py +11 -0
  3. bead/active_learning/__init__.py +15 -0
  4. bead/active_learning/config.py +231 -0
  5. bead/active_learning/loop.py +566 -0
  6. bead/active_learning/models/__init__.py +24 -0
  7. bead/active_learning/models/base.py +852 -0
  8. bead/active_learning/models/binary.py +910 -0
  9. bead/active_learning/models/categorical.py +943 -0
  10. bead/active_learning/models/cloze.py +862 -0
  11. bead/active_learning/models/forced_choice.py +956 -0
  12. bead/active_learning/models/free_text.py +773 -0
  13. bead/active_learning/models/lora.py +365 -0
  14. bead/active_learning/models/magnitude.py +835 -0
  15. bead/active_learning/models/multi_select.py +795 -0
  16. bead/active_learning/models/ordinal_scale.py +811 -0
  17. bead/active_learning/models/peft_adapter.py +155 -0
  18. bead/active_learning/models/random_effects.py +639 -0
  19. bead/active_learning/selection.py +354 -0
  20. bead/active_learning/strategies.py +391 -0
  21. bead/active_learning/trainers/__init__.py +26 -0
  22. bead/active_learning/trainers/base.py +210 -0
  23. bead/active_learning/trainers/data_collator.py +172 -0
  24. bead/active_learning/trainers/dataset_utils.py +261 -0
  25. bead/active_learning/trainers/huggingface.py +304 -0
  26. bead/active_learning/trainers/lightning.py +324 -0
  27. bead/active_learning/trainers/metrics.py +424 -0
  28. bead/active_learning/trainers/mixed_effects.py +551 -0
  29. bead/active_learning/trainers/model_wrapper.py +509 -0
  30. bead/active_learning/trainers/registry.py +104 -0
  31. bead/adapters/__init__.py +11 -0
  32. bead/adapters/huggingface.py +61 -0
  33. bead/behavioral/__init__.py +116 -0
  34. bead/behavioral/analytics.py +646 -0
  35. bead/behavioral/extraction.py +343 -0
  36. bead/behavioral/merging.py +343 -0
  37. bead/cli/__init__.py +11 -0
  38. bead/cli/active_learning.py +513 -0
  39. bead/cli/active_learning_commands.py +779 -0
  40. bead/cli/completion.py +359 -0
  41. bead/cli/config.py +624 -0
  42. bead/cli/constraint_builders.py +286 -0
  43. bead/cli/deployment.py +859 -0
  44. bead/cli/deployment_trials.py +493 -0
  45. bead/cli/deployment_ui.py +332 -0
  46. bead/cli/display.py +378 -0
  47. bead/cli/items.py +960 -0
  48. bead/cli/items_factories.py +776 -0
  49. bead/cli/list_constraints.py +714 -0
  50. bead/cli/lists.py +490 -0
  51. bead/cli/main.py +430 -0
  52. bead/cli/models.py +877 -0
  53. bead/cli/resource_loaders.py +621 -0
  54. bead/cli/resources.py +1036 -0
  55. bead/cli/shell.py +356 -0
  56. bead/cli/simulate.py +840 -0
  57. bead/cli/templates.py +1158 -0
  58. bead/cli/training.py +1080 -0
  59. bead/cli/utils.py +614 -0
  60. bead/cli/workflow.py +1273 -0
  61. bead/config/__init__.py +68 -0
  62. bead/config/active_learning.py +1009 -0
  63. bead/config/config.py +192 -0
  64. bead/config/defaults.py +118 -0
  65. bead/config/deployment.py +217 -0
  66. bead/config/env.py +147 -0
  67. bead/config/item.py +45 -0
  68. bead/config/list.py +193 -0
  69. bead/config/loader.py +149 -0
  70. bead/config/logging.py +42 -0
  71. bead/config/model.py +49 -0
  72. bead/config/paths.py +46 -0
  73. bead/config/profiles.py +320 -0
  74. bead/config/resources.py +47 -0
  75. bead/config/serialization.py +210 -0
  76. bead/config/simulation.py +206 -0
  77. bead/config/template.py +238 -0
  78. bead/config/validation.py +267 -0
  79. bead/data/__init__.py +65 -0
  80. bead/data/base.py +87 -0
  81. bead/data/identifiers.py +97 -0
  82. bead/data/language_codes.py +61 -0
  83. bead/data/metadata.py +270 -0
  84. bead/data/range.py +123 -0
  85. bead/data/repository.py +358 -0
  86. bead/data/serialization.py +249 -0
  87. bead/data/timestamps.py +89 -0
  88. bead/data/validation.py +349 -0
  89. bead/data_collection/__init__.py +11 -0
  90. bead/data_collection/jatos.py +223 -0
  91. bead/data_collection/merger.py +154 -0
  92. bead/data_collection/prolific.py +198 -0
  93. bead/deployment/__init__.py +5 -0
  94. bead/deployment/distribution.py +402 -0
  95. bead/deployment/jatos/__init__.py +1 -0
  96. bead/deployment/jatos/api.py +200 -0
  97. bead/deployment/jatos/exporter.py +210 -0
  98. bead/deployment/jspsych/__init__.py +9 -0
  99. bead/deployment/jspsych/biome.json +44 -0
  100. bead/deployment/jspsych/config.py +411 -0
  101. bead/deployment/jspsych/generator.py +598 -0
  102. bead/deployment/jspsych/package.json +51 -0
  103. bead/deployment/jspsych/pnpm-lock.yaml +2141 -0
  104. bead/deployment/jspsych/randomizer.py +299 -0
  105. bead/deployment/jspsych/src/lib/list-distributor.test.ts +327 -0
  106. bead/deployment/jspsych/src/lib/list-distributor.ts +1282 -0
  107. bead/deployment/jspsych/src/lib/randomizer.test.ts +232 -0
  108. bead/deployment/jspsych/src/lib/randomizer.ts +367 -0
  109. bead/deployment/jspsych/src/plugins/cloze-dropdown.ts +252 -0
  110. bead/deployment/jspsych/src/plugins/forced-choice.ts +265 -0
  111. bead/deployment/jspsych/src/plugins/plugins.test.ts +141 -0
  112. bead/deployment/jspsych/src/plugins/rating.ts +248 -0
  113. bead/deployment/jspsych/src/slopit/index.ts +9 -0
  114. bead/deployment/jspsych/src/types/jatos.d.ts +256 -0
  115. bead/deployment/jspsych/src/types/jspsych.d.ts +228 -0
  116. bead/deployment/jspsych/templates/experiment.css +1 -0
  117. bead/deployment/jspsych/templates/experiment.js.template +289 -0
  118. bead/deployment/jspsych/templates/index.html +51 -0
  119. bead/deployment/jspsych/templates/randomizer.js +241 -0
  120. bead/deployment/jspsych/templates/randomizer.js.template +313 -0
  121. bead/deployment/jspsych/trials.py +723 -0
  122. bead/deployment/jspsych/tsconfig.json +23 -0
  123. bead/deployment/jspsych/tsup.config.ts +30 -0
  124. bead/deployment/jspsych/ui/__init__.py +1 -0
  125. bead/deployment/jspsych/ui/components.py +383 -0
  126. bead/deployment/jspsych/ui/styles.py +411 -0
  127. bead/dsl/__init__.py +80 -0
  128. bead/dsl/ast.py +168 -0
  129. bead/dsl/context.py +178 -0
  130. bead/dsl/errors.py +71 -0
  131. bead/dsl/evaluator.py +570 -0
  132. bead/dsl/grammar.lark +81 -0
  133. bead/dsl/parser.py +231 -0
  134. bead/dsl/stdlib.py +929 -0
  135. bead/evaluation/__init__.py +13 -0
  136. bead/evaluation/convergence.py +485 -0
  137. bead/evaluation/interannotator.py +398 -0
  138. bead/items/__init__.py +40 -0
  139. bead/items/adapters/__init__.py +70 -0
  140. bead/items/adapters/anthropic.py +224 -0
  141. bead/items/adapters/api_utils.py +167 -0
  142. bead/items/adapters/base.py +216 -0
  143. bead/items/adapters/google.py +259 -0
  144. bead/items/adapters/huggingface.py +1074 -0
  145. bead/items/adapters/openai.py +323 -0
  146. bead/items/adapters/registry.py +202 -0
  147. bead/items/adapters/sentence_transformers.py +224 -0
  148. bead/items/adapters/togetherai.py +309 -0
  149. bead/items/binary.py +515 -0
  150. bead/items/cache.py +558 -0
  151. bead/items/categorical.py +593 -0
  152. bead/items/cloze.py +757 -0
  153. bead/items/constructor.py +784 -0
  154. bead/items/forced_choice.py +413 -0
  155. bead/items/free_text.py +681 -0
  156. bead/items/generation.py +432 -0
  157. bead/items/item.py +396 -0
  158. bead/items/item_template.py +787 -0
  159. bead/items/magnitude.py +573 -0
  160. bead/items/multi_select.py +621 -0
  161. bead/items/ordinal_scale.py +569 -0
  162. bead/items/scoring.py +448 -0
  163. bead/items/validation.py +723 -0
  164. bead/lists/__init__.py +30 -0
  165. bead/lists/balancer.py +263 -0
  166. bead/lists/constraints.py +1067 -0
  167. bead/lists/experiment_list.py +286 -0
  168. bead/lists/list_collection.py +378 -0
  169. bead/lists/partitioner.py +1141 -0
  170. bead/lists/stratification.py +254 -0
  171. bead/participants/__init__.py +73 -0
  172. bead/participants/collection.py +699 -0
  173. bead/participants/merging.py +312 -0
  174. bead/participants/metadata_spec.py +491 -0
  175. bead/participants/models.py +276 -0
  176. bead/resources/__init__.py +29 -0
  177. bead/resources/adapters/__init__.py +19 -0
  178. bead/resources/adapters/base.py +104 -0
  179. bead/resources/adapters/cache.py +128 -0
  180. bead/resources/adapters/glazing.py +508 -0
  181. bead/resources/adapters/registry.py +117 -0
  182. bead/resources/adapters/unimorph.py +796 -0
  183. bead/resources/classification.py +856 -0
  184. bead/resources/constraint_builders.py +329 -0
  185. bead/resources/constraints.py +165 -0
  186. bead/resources/lexical_item.py +223 -0
  187. bead/resources/lexicon.py +744 -0
  188. bead/resources/loaders.py +209 -0
  189. bead/resources/template.py +441 -0
  190. bead/resources/template_collection.py +707 -0
  191. bead/resources/template_generation.py +349 -0
  192. bead/simulation/__init__.py +29 -0
  193. bead/simulation/annotators/__init__.py +15 -0
  194. bead/simulation/annotators/base.py +175 -0
  195. bead/simulation/annotators/distance_based.py +135 -0
  196. bead/simulation/annotators/lm_based.py +114 -0
  197. bead/simulation/annotators/oracle.py +182 -0
  198. bead/simulation/annotators/random.py +181 -0
  199. bead/simulation/dsl_extension/__init__.py +3 -0
  200. bead/simulation/noise_models/__init__.py +13 -0
  201. bead/simulation/noise_models/base.py +42 -0
  202. bead/simulation/noise_models/random_noise.py +82 -0
  203. bead/simulation/noise_models/systematic.py +132 -0
  204. bead/simulation/noise_models/temperature.py +86 -0
  205. bead/simulation/runner.py +144 -0
  206. bead/simulation/strategies/__init__.py +23 -0
  207. bead/simulation/strategies/base.py +123 -0
  208. bead/simulation/strategies/binary.py +103 -0
  209. bead/simulation/strategies/categorical.py +123 -0
  210. bead/simulation/strategies/cloze.py +224 -0
  211. bead/simulation/strategies/forced_choice.py +127 -0
  212. bead/simulation/strategies/free_text.py +105 -0
  213. bead/simulation/strategies/magnitude.py +116 -0
  214. bead/simulation/strategies/multi_select.py +129 -0
  215. bead/simulation/strategies/ordinal_scale.py +131 -0
  216. bead/templates/__init__.py +27 -0
  217. bead/templates/adapters/__init__.py +17 -0
  218. bead/templates/adapters/base.py +128 -0
  219. bead/templates/adapters/cache.py +178 -0
  220. bead/templates/adapters/huggingface.py +312 -0
  221. bead/templates/combinatorics.py +103 -0
  222. bead/templates/filler.py +605 -0
  223. bead/templates/renderers.py +177 -0
  224. bead/templates/resolver.py +178 -0
  225. bead/templates/strategies.py +1806 -0
  226. bead/templates/streaming.py +195 -0
  227. bead-0.1.0.dist-info/METADATA +212 -0
  228. bead-0.1.0.dist-info/RECORD +231 -0
  229. bead-0.1.0.dist-info/WHEEL +4 -0
  230. bead-0.1.0.dist-info/entry_points.txt +2 -0
  231. bead-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,177 @@
1
+ """Template rendering system with plugin support.
2
+
3
+ This module provides the base template rendering interface and a simple
4
+ default implementation. Language-specific or custom rendering logic should
5
+ be implemented as external plugins.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from abc import ABC, abstractmethod
11
+ from collections.abc import Mapping
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING:
15
+ from bead.resources.lexical_item import LexicalItem
16
+ from bead.resources.template import Slot
17
+
18
+
19
+ class TemplateRenderer(ABC):
20
+ """Base class for template renderers.
21
+
22
+ Custom renderers should subclass this and implement the render() method.
23
+ This allows for language-specific or experiment-specific rendering logic
24
+ to be implemented as external plugins.
25
+
26
+ Examples
27
+ --------
28
+ >>> from bead.templates.renderers import TemplateRenderer
29
+ >>> class CustomRenderer(TemplateRenderer):
30
+ ... def render(self, template_string, slot_fillers, template_slots):
31
+ ... # Custom rendering logic here
32
+ ... return rendered_text
33
+ """
34
+
35
+ @abstractmethod
36
+ def render(
37
+ self,
38
+ template_string: str,
39
+ slot_fillers: Mapping[str, LexicalItem],
40
+ template_slots: Mapping[str, Slot],
41
+ ) -> str:
42
+ """Render template with slot fillers.
43
+
44
+ Parameters
45
+ ----------
46
+ template_string : str
47
+ Template string with {slot_name} placeholders.
48
+ slot_fillers : Mapping[str, LexicalItem]
49
+ Mapping from slot names to lexical items that fill them.
50
+ template_slots : Mapping[str, Slot]
51
+ Mapping from slot names to slot definitions. Provides access
52
+ to slot constraints and metadata for context-aware rendering.
53
+
54
+ Returns
55
+ -------
56
+ str
57
+ Rendered text with placeholders replaced.
58
+
59
+ Examples
60
+ --------
61
+ >>> renderer = DefaultRenderer()
62
+ >>> from bead.resources.lexical_item import LexicalItem
63
+ >>> from bead.resources.template import Slot
64
+ >>> fillers = {
65
+ ... "subj": LexicalItem(lemma="cat", language_code="eng"),
66
+ ... "verb": LexicalItem(lemma="run", form="runs", language_code="eng")
67
+ ... }
68
+ >>> slots = {
69
+ ... "subj": Slot(name="subj"),
70
+ ... "verb": Slot(name="verb")
71
+ ... }
72
+ >>> renderer.render("{subj} {verb}", fillers, slots)
73
+ 'cat runs'
74
+ """
75
+ ...
76
+
77
+
78
+ class DefaultRenderer(TemplateRenderer):
79
+ """Default renderer using simple slot substitution.
80
+
81
+ Uses item.form if available, otherwise item.lemma.
82
+ This is a language-agnostic implementation suitable for most use cases.
83
+
84
+ Examples
85
+ --------
86
+ >>> from bead.templates.renderers import DefaultRenderer
87
+ >>> from bead.resources.lexical_item import LexicalItem
88
+ >>> from bead.resources.template import Slot
89
+ >>> renderer = DefaultRenderer()
90
+ >>>
91
+ >>> # Example 1: Basic rendering with lemmas
92
+ >>> fillers = {
93
+ ... "det": LexicalItem(lemma="the", language_code="eng"),
94
+ ... "noun": LexicalItem(lemma="cat", language_code="eng"),
95
+ ... "verb": LexicalItem(lemma="run", language_code="eng")
96
+ ... }
97
+ >>> slots = {name: Slot(name=name) for name in fillers.keys()}
98
+ >>> renderer.render("{det} {noun} {verb}", fillers, slots)
99
+ 'the cat run'
100
+ >>>
101
+ >>> # Example 2: Rendering with forms (inflected)
102
+ >>> fillers_with_forms = {
103
+ ... "det": LexicalItem(lemma="the", language_code="eng"),
104
+ ... "noun": LexicalItem(lemma="cat", form="cats", language_code="eng"),
105
+ ... "verb": LexicalItem(lemma="run", form="runs", language_code="eng")
106
+ ... }
107
+ >>> renderer.render("{det} {noun} {verb}", fillers_with_forms, slots)
108
+ 'the cats runs'
109
+ """
110
+
111
+ def render(
112
+ self,
113
+ template_string: str,
114
+ slot_fillers: Mapping[str, LexicalItem],
115
+ template_slots: Mapping[str, Slot],
116
+ ) -> str:
117
+ """Render template with simple slot substitution.
118
+
119
+ Uses item.form if available, otherwise item.lemma. This provides
120
+ a straightforward rendering suitable for most templates without
121
+ special language-specific handling.
122
+
123
+ Parameters
124
+ ----------
125
+ template_string : str
126
+ Template string with {slot_name} placeholders.
127
+ slot_fillers : Mapping[str, LexicalItem]
128
+ Mapping from slot names to lexical items.
129
+ template_slots : Mapping[str, Slot]
130
+ Mapping from slot names to slot definitions (unused in default
131
+ implementation, but provided for subclasses).
132
+
133
+ Returns
134
+ -------
135
+ str
136
+ Rendered text with placeholders replaced by item forms or lemmas.
137
+
138
+ Notes
139
+ -----
140
+ This renderer prioritizes item.form over item.lemma to support
141
+ morphological inflection. If item.form is None, it falls back to
142
+ item.lemma.
143
+
144
+ The template_slots parameter is not used in the default implementation
145
+ but is available for subclasses that need slot metadata for rendering.
146
+
147
+ Examples
148
+ --------
149
+ >>> renderer = DefaultRenderer()
150
+ >>> from bead.resources.lexical_item import LexicalItem
151
+ >>> from bead.resources.template import Slot
152
+ >>>
153
+ >>> # Form takes precedence over lemma
154
+ >>> fillers = {
155
+ ... "verb": LexicalItem(lemma="walk", form="walked", language_code="eng")
156
+ ... }
157
+ >>> slots = {"verb": Slot(name="verb")}
158
+ >>> renderer.render("{verb}", fillers, slots)
159
+ 'walked'
160
+ >>>
161
+ >>> # Falls back to lemma when form is None
162
+ >>> fillers_no_form = {
163
+ ... "verb": LexicalItem(lemma="walk", form=None, language_code="eng")
164
+ ... }
165
+ >>> renderer.render("{verb}", fillers_no_form, slots)
166
+ 'walk'
167
+ """
168
+ result = template_string
169
+
170
+ for slot_name, item in slot_fillers.items():
171
+ placeholder = f"{{{slot_name}}}"
172
+ if placeholder in result:
173
+ # Use form if available, otherwise lemma
174
+ surface = item.form if item.form is not None else item.lemma
175
+ result = result.replace(placeholder, surface)
176
+
177
+ return result
@@ -0,0 +1,178 @@
1
+ """Constraint resolution for template slot filling.
2
+
3
+ This module provides the ConstraintResolver class for evaluating constraints
4
+ against lexical items to determine which items satisfy template slot requirements.
5
+ The resolver is now a thin wrapper around DSLEvaluator.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ from bead.dsl.evaluator import DSLEvaluator
13
+ from bead.resources.constraints import Constraint
14
+
15
+ if TYPE_CHECKING:
16
+ from bead.resources.constraints import ContextValue
17
+ from bead.resources.lexical_item import LexicalItem
18
+
19
+
20
+ class ConstraintResolver:
21
+ """Simplified resolver that wraps DSLEvaluator.
22
+
23
+ The ConstraintResolver evaluates DSL constraint expressions against
24
+ lexical items and filled slots. It provides two main methods:
25
+ - evaluate_slot_constraints: for single-slot constraints
26
+ - evaluate_template_constraints: for multi-slot constraints
27
+
28
+ All constraint logic is delegated to the DSL evaluator.
29
+
30
+ Examples
31
+ --------
32
+ >>> from bead.resources.models import LexicalItem
33
+ >>> from bead.resources.constraints import Constraint
34
+ >>> resolver = ConstraintResolver()
35
+ >>> item = LexicalItem(lemma="walk", pos="VERB")
36
+ >>> constraints = [
37
+ ... Constraint(expression="self.pos == 'VERB'")
38
+ ... ]
39
+ >>> resolver.evaluate_slot_constraints(item, constraints)
40
+ True
41
+ """
42
+
43
+ def __init__(self) -> None:
44
+ """Initialize resolver with DSL evaluator."""
45
+ self.dsl_evaluator = DSLEvaluator()
46
+
47
+ def evaluate_slot_constraints(
48
+ self,
49
+ item: LexicalItem,
50
+ constraints: list[Constraint],
51
+ context: dict[str, ContextValue] | None = None,
52
+ ) -> bool:
53
+ """Evaluate single-slot constraints.
54
+
55
+ Single-slot constraints are evaluated with 'self' referring to
56
+ the lexical item being checked. The item is available as 'self'
57
+ in the DSL expression context.
58
+
59
+ Parameters
60
+ ----------
61
+ item : LexicalItem
62
+ Lexical item to evaluate constraints against.
63
+ constraints : list[Constraint]
64
+ Single-slot constraints from Slot.constraints.
65
+ context : dict[str, ContextValue] | None
66
+ Additional context variables (optional).
67
+
68
+ Returns
69
+ -------
70
+ bool
71
+ True if all constraints are satisfied, False otherwise.
72
+
73
+ Examples
74
+ --------
75
+ >>> from bead.resources.models import LexicalItem
76
+ >>> from bead.resources.constraints import Constraint
77
+ >>> resolver = ConstraintResolver()
78
+ >>> item = LexicalItem(lemma="walk", pos="VERB")
79
+ >>> constraints = [
80
+ ... Constraint(
81
+ ... expression="self.lemma in motion_verbs",
82
+ ... context={"motion_verbs": {"walk", "run", "jump"}}
83
+ ... )
84
+ ... ]
85
+ >>> resolver.evaluate_slot_constraints(item, constraints)
86
+ True
87
+ """
88
+ for constraint in constraints:
89
+ # build evaluation context
90
+ eval_context: dict[str, Any] = {
91
+ "self": item,
92
+ **constraint.context,
93
+ }
94
+ if context:
95
+ eval_context.update(context)
96
+
97
+ # evaluate constraint
98
+ result = self.dsl_evaluator.evaluate(constraint.expression, eval_context)
99
+ if not result:
100
+ return False
101
+
102
+ return True
103
+
104
+ def evaluate_template_constraints(
105
+ self,
106
+ filled_slots: dict[str, LexicalItem],
107
+ constraints: list[Constraint],
108
+ context: dict[str, ContextValue] | None = None,
109
+ ) -> bool:
110
+ """Evaluate multi-slot constraints.
111
+
112
+ Multi-slot constraints are evaluated with slot names as variables.
113
+ For example, a constraint like "subject.features.number == verb.features.number"
114
+ would have access to both the 'subject' and 'verb' slot fillers.
115
+
116
+ Parameters
117
+ ----------
118
+ filled_slots : dict[str, LexicalItem]
119
+ Dictionary mapping slot names to their filled items.
120
+ constraints : list[Constraint]
121
+ Multi-slot constraints from Template.constraints.
122
+ context : dict[str, ContextValue] | None
123
+ Additional context variables (optional).
124
+
125
+ Returns
126
+ -------
127
+ bool
128
+ True if all constraints are satisfied, False otherwise.
129
+
130
+ Examples
131
+ --------
132
+ >>> from bead.resources.models import LexicalItem
133
+ >>> from bead.resources.constraints import Constraint
134
+ >>> resolver = ConstraintResolver()
135
+ >>> subject = LexicalItem(
136
+ ... lemma="dog",
137
+ ... pos="NOUN",
138
+ ... features={"number": "singular"}
139
+ ... )
140
+ >>> verb = LexicalItem(
141
+ ... lemma="runs",
142
+ ... pos="VERB",
143
+ ... features={"number": "singular"}
144
+ ... )
145
+ >>> filled = {"subject": subject, "verb": verb}
146
+ >>> constraints = [
147
+ ... Constraint(
148
+ ... expression="subject.features.number == verb.features.number"
149
+ ... )
150
+ ... ]
151
+ >>> resolver.evaluate_template_constraints(filled, constraints)
152
+ True
153
+ """
154
+ for constraint in constraints:
155
+ # build evaluation context with slot fillers
156
+ eval_context: dict[str, Any] = {
157
+ **filled_slots,
158
+ **constraint.context,
159
+ }
160
+ if context:
161
+ eval_context.update(context)
162
+
163
+ # evaluate constraint
164
+ result = self.dsl_evaluator.evaluate(constraint.expression, eval_context)
165
+ if not result:
166
+ return False
167
+
168
+ return True
169
+
170
+ def clear_cache(self) -> None:
171
+ """Clear DSL evaluator's compiled expression cache.
172
+
173
+ Examples
174
+ --------
175
+ >>> resolver = ConstraintResolver()
176
+ >>> resolver.clear_cache()
177
+ """
178
+ self.dsl_evaluator.clear_cache()