edsl 0.1.30.dev4__py3-none-any.whl → 0.1.31__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.
- edsl/__version__.py +1 -1
- edsl/agents/Invigilator.py +7 -2
- edsl/agents/PromptConstructionMixin.py +18 -1
- edsl/config.py +4 -0
- edsl/conjure/Conjure.py +6 -0
- edsl/coop/coop.py +4 -0
- edsl/coop/utils.py +9 -1
- edsl/data/CacheHandler.py +3 -4
- edsl/enums.py +2 -0
- edsl/inference_services/DeepInfraService.py +6 -91
- edsl/inference_services/GroqService.py +18 -0
- edsl/inference_services/InferenceServicesCollection.py +13 -5
- edsl/inference_services/OpenAIService.py +64 -21
- edsl/inference_services/registry.py +2 -1
- edsl/jobs/Jobs.py +80 -33
- edsl/jobs/buckets/TokenBucket.py +24 -5
- edsl/jobs/interviews/Interview.py +122 -75
- edsl/jobs/interviews/InterviewExceptionEntry.py +101 -0
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +58 -52
- edsl/jobs/interviews/interview_exception_tracking.py +68 -10
- edsl/jobs/runners/JobsRunnerAsyncio.py +112 -81
- edsl/jobs/runners/JobsRunnerStatusData.py +0 -237
- edsl/jobs/runners/JobsRunnerStatusMixin.py +291 -35
- edsl/jobs/tasks/QuestionTaskCreator.py +1 -5
- edsl/jobs/tasks/TaskCreators.py +8 -2
- edsl/jobs/tasks/TaskHistory.py +145 -1
- edsl/language_models/LanguageModel.py +135 -75
- edsl/language_models/ModelList.py +8 -2
- edsl/language_models/registry.py +16 -0
- edsl/questions/QuestionFunctional.py +34 -2
- edsl/questions/QuestionMultipleChoice.py +58 -8
- edsl/questions/QuestionNumerical.py +0 -1
- edsl/questions/descriptors.py +42 -2
- edsl/results/DatasetExportMixin.py +258 -75
- edsl/results/Result.py +53 -5
- edsl/results/Results.py +66 -27
- edsl/results/ResultsToolsMixin.py +1 -1
- edsl/scenarios/Scenario.py +14 -0
- edsl/scenarios/ScenarioList.py +59 -21
- edsl/scenarios/ScenarioListExportMixin.py +16 -5
- edsl/scenarios/ScenarioListPdfMixin.py +3 -0
- edsl/study/Study.py +2 -2
- edsl/surveys/Survey.py +35 -1
- {edsl-0.1.30.dev4.dist-info → edsl-0.1.31.dist-info}/METADATA +4 -2
- {edsl-0.1.30.dev4.dist-info → edsl-0.1.31.dist-info}/RECORD +47 -45
- {edsl-0.1.30.dev4.dist-info → edsl-0.1.31.dist-info}/WHEEL +1 -1
- {edsl-0.1.30.dev4.dist-info → edsl-0.1.31.dist-info}/LICENSE +0 -0
edsl/results/Results.py
CHANGED
@@ -604,6 +604,38 @@ class Results(UserList, Mixins, Base):
|
|
604
604
|
self = self.add_column(key, values)
|
605
605
|
return self
|
606
606
|
|
607
|
+
@staticmethod
|
608
|
+
def _create_evaluator(
|
609
|
+
result: Result, functions_dict: Optional[dict] = None
|
610
|
+
) -> EvalWithCompoundTypes:
|
611
|
+
"""Create an evaluator for the expression.
|
612
|
+
|
613
|
+
>>> from unittest.mock import Mock
|
614
|
+
>>> result = Mock()
|
615
|
+
>>> result.combined_dict = {'how_feeling': 'OK'}
|
616
|
+
|
617
|
+
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
618
|
+
>>> evaluator.eval("how_feeling == 'OK'")
|
619
|
+
True
|
620
|
+
|
621
|
+
>>> result.combined_dict = {'answer': {'how_feeling': 'OK'}}
|
622
|
+
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
623
|
+
>>> evaluator.eval("answer.how_feeling== 'OK'")
|
624
|
+
True
|
625
|
+
|
626
|
+
Note that you need to refer to the answer dictionary in the expression.
|
627
|
+
|
628
|
+
>>> evaluator.eval("how_feeling== 'OK'")
|
629
|
+
Traceback (most recent call last):
|
630
|
+
...
|
631
|
+
simpleeval.NameNotDefined: 'how_feeling' is not defined for expression 'how_feeling== 'OK''
|
632
|
+
"""
|
633
|
+
if functions_dict is None:
|
634
|
+
functions_dict = {}
|
635
|
+
return EvalWithCompoundTypes(
|
636
|
+
names=result.combined_dict, functions=functions_dict
|
637
|
+
)
|
638
|
+
|
607
639
|
def mutate(
|
608
640
|
self, new_var_string: str, functions_dict: Optional[dict] = None
|
609
641
|
) -> Results:
|
@@ -636,13 +668,8 @@ class Results(UserList, Mixins, Base):
|
|
636
668
|
# create the evaluator
|
637
669
|
functions_dict = functions_dict or {}
|
638
670
|
|
639
|
-
def create_evaluator(result: Result) -> EvalWithCompoundTypes:
|
640
|
-
return EvalWithCompoundTypes(
|
641
|
-
names=result.combined_dict, functions=functions_dict
|
642
|
-
)
|
643
|
-
|
644
671
|
def new_result(old_result: "Result", var_name: str) -> "Result":
|
645
|
-
evaluator =
|
672
|
+
evaluator = self._create_evaluator(old_result, functions_dict)
|
646
673
|
value = evaluator.eval(expression)
|
647
674
|
new_result = old_result.copy()
|
648
675
|
new_result["answer"][var_name] = value
|
@@ -742,6 +769,9 @@ class Results(UserList, Mixins, Base):
|
|
742
769
|
>>> results = Results.example()
|
743
770
|
>>> results.select('how_feeling')
|
744
771
|
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
772
|
+
|
773
|
+
>>> results.select('how_feeling', 'model', 'how_feeling')
|
774
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'model.model': ['gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
745
775
|
"""
|
746
776
|
|
747
777
|
if len(self) == 0:
|
@@ -799,10 +829,19 @@ class Results(UserList, Mixins, Base):
|
|
799
829
|
# Return the index of this key in the list_of_keys
|
800
830
|
return items_in_order.index(single_key)
|
801
831
|
|
802
|
-
sorted(new_data, key=sort_by_key_order)
|
832
|
+
# sorted(new_data, key=sort_by_key_order)
|
803
833
|
from edsl.results.Dataset import Dataset
|
804
834
|
|
805
|
-
|
835
|
+
sorted_new_data = []
|
836
|
+
|
837
|
+
# WORKS but slow
|
838
|
+
for key in items_in_order:
|
839
|
+
for d in new_data:
|
840
|
+
if key in d:
|
841
|
+
sorted_new_data.append(d)
|
842
|
+
break
|
843
|
+
|
844
|
+
return Dataset(sorted_new_data)
|
806
845
|
|
807
846
|
def sort_by(self, *columns: str, reverse: bool = False) -> Results:
|
808
847
|
import warnings
|
@@ -917,29 +956,29 @@ class Results(UserList, Mixins, Base):
|
|
917
956
|
"You must use '==' instead of '=' in the filter expression."
|
918
957
|
)
|
919
958
|
|
920
|
-
def create_evaluator(result):
|
921
|
-
"""Create an evaluator for the given result.
|
922
|
-
The 'combined_dict' is a mapping of all values for that Result object.
|
923
|
-
"""
|
924
|
-
return EvalWithCompoundTypes(names=result.combined_dict)
|
925
|
-
|
926
959
|
try:
|
927
960
|
# iterates through all the results and evaluates the expression
|
928
|
-
new_data = [
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
961
|
+
new_data = []
|
962
|
+
for result in self.data:
|
963
|
+
evaluator = self._create_evaluator(result)
|
964
|
+
result.check_expression(expression) # check expression
|
965
|
+
if evaluator.eval(expression):
|
966
|
+
new_data.append(result)
|
967
|
+
|
968
|
+
except ValueError as e:
|
969
|
+
raise ResultsFilterError(
|
970
|
+
f"Error in filter. Exception:{e}",
|
971
|
+
f"The expression you provided was: {expression}",
|
972
|
+
"See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.",
|
973
|
+
)
|
933
974
|
except Exception as e:
|
934
975
|
raise ResultsFilterError(
|
935
|
-
f"""Error in filter. Exception:{e}.
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.
|
942
|
-
"""
|
976
|
+
f"""Error in filter. Exception:{e}.""",
|
977
|
+
f"""The expression you provided was: {expression}.""",
|
978
|
+
"""Please make sure that the expression is a valid Python expression that evaluates to a boolean.""",
|
979
|
+
"""For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'., """,
|
980
|
+
"""However, 'how_feeling = "Great"' is not a valid expression.""",
|
981
|
+
"""See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.""",
|
943
982
|
)
|
944
983
|
|
945
984
|
if len(new_data) == 0:
|
@@ -37,12 +37,12 @@ class ResultsToolsMixin:
|
|
37
37
|
print_exceptions=False,
|
38
38
|
) -> dict:
|
39
39
|
from edsl import ScenarioList
|
40
|
+
from edsl import QuestionCheckBox
|
40
41
|
|
41
42
|
values = self.select(field).to_list()
|
42
43
|
scenarios = ScenarioList.from_list("field", values).add_value(
|
43
44
|
"context", context
|
44
45
|
)
|
45
|
-
|
46
46
|
q = QuestionCheckBox(
|
47
47
|
question_text="""
|
48
48
|
{{ context }}
|
edsl/scenarios/Scenario.py
CHANGED
@@ -182,6 +182,19 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
182
182
|
new_scenario[key] = self[key]
|
183
183
|
return new_scenario
|
184
184
|
|
185
|
+
@classmethod
|
186
|
+
def from_url(cls, url: str, field_name: Optional[str] = "text") -> "Scenario":
|
187
|
+
"""Creates a scenario from a URL.
|
188
|
+
|
189
|
+
:param url: The URL to create the scenario from.
|
190
|
+
:param field_name: The field name to use for the text.
|
191
|
+
|
192
|
+
"""
|
193
|
+
import requests
|
194
|
+
|
195
|
+
text = requests.get(url).text
|
196
|
+
return cls({"url": url, field_name: text})
|
197
|
+
|
185
198
|
@classmethod
|
186
199
|
def from_image(cls, image_path: str) -> str:
|
187
200
|
"""Creates a scenario with a base64 encoding of an image.
|
@@ -207,6 +220,7 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
207
220
|
@classmethod
|
208
221
|
def from_pdf(cls, pdf_path):
|
209
222
|
import fitz # PyMuPDF
|
223
|
+
from edsl import Scenario
|
210
224
|
|
211
225
|
# Ensure the file exists
|
212
226
|
if not os.path.exists(pdf_path):
|
edsl/scenarios/ScenarioList.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
"""A list of Scenarios to be used in a survey."""
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
|
+
from typing import Any, Optional, Union, List, Callable
|
4
5
|
import csv
|
5
6
|
import random
|
6
7
|
from collections import UserList, Counter
|
7
8
|
from collections.abc import Iterable
|
9
|
+
|
8
10
|
from simpleeval import EvalWithCompoundTypes
|
9
|
-
|
11
|
+
|
10
12
|
from edsl.Base import Base
|
11
13
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
12
14
|
from edsl.scenarios.Scenario import Scenario
|
@@ -58,7 +60,13 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
58
60
|
return f"ScenarioList({self.data})"
|
59
61
|
|
60
62
|
def __mul__(self, other: ScenarioList) -> ScenarioList:
|
61
|
-
"""Takes the cross product of two ScenarioLists.
|
63
|
+
"""Takes the cross product of two ScenarioLists.
|
64
|
+
|
65
|
+
>>> s1 = ScenarioList.from_list("a", [1, 2])
|
66
|
+
>>> s2 = ScenarioList.from_list("b", [3, 4])
|
67
|
+
>>> s1 * s2
|
68
|
+
ScenarioList([Scenario({'a': 1, 'b': 3}), Scenario({'a': 1, 'b': 4}), Scenario({'a': 2, 'b': 3}), Scenario({'a': 2, 'b': 4})])
|
69
|
+
"""
|
62
70
|
from itertools import product
|
63
71
|
|
64
72
|
new_sl = []
|
@@ -79,7 +87,12 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
79
87
|
return self.__mul__(other)
|
80
88
|
|
81
89
|
def shuffle(self, seed: Optional[str] = "edsl") -> ScenarioList:
|
82
|
-
"""Shuffle the ScenarioList.
|
90
|
+
"""Shuffle the ScenarioList.
|
91
|
+
|
92
|
+
>>> s = ScenarioList.from_list("a", [1,2,3,4])
|
93
|
+
>>> s.shuffle()
|
94
|
+
ScenarioList([Scenario({'a': 3}), Scenario({'a': 4}), Scenario({'a': 1}), Scenario({'a': 2})])
|
95
|
+
"""
|
83
96
|
random.seed(seed)
|
84
97
|
random.shuffle(self.data)
|
85
98
|
return self
|
@@ -107,10 +120,14 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
107
120
|
return dict(Counter([scenario[field] for scenario in self]))
|
108
121
|
|
109
122
|
def sample(self, n: int, seed="edsl") -> ScenarioList:
|
110
|
-
"""Return a random sample from the ScenarioList
|
123
|
+
"""Return a random sample from the ScenarioList
|
111
124
|
|
112
|
-
|
113
|
-
|
125
|
+
>>> s = ScenarioList.from_list("a", [1,2,3,4,5,6])
|
126
|
+
>>> s.sample(3)
|
127
|
+
ScenarioList([Scenario({'a': 2}), Scenario({'a': 1}), Scenario({'a': 3})])
|
128
|
+
"""
|
129
|
+
|
130
|
+
random.seed(seed)
|
114
131
|
|
115
132
|
return ScenarioList(random.sample(self.data, n))
|
116
133
|
|
@@ -136,7 +153,9 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
136
153
|
new_scenarios.append(new_scenario)
|
137
154
|
return ScenarioList(new_scenarios)
|
138
155
|
|
139
|
-
def mutate(
|
156
|
+
def mutate(
|
157
|
+
self, new_var_string: str, functions_dict: Optional[dict[str, Callable]] = None
|
158
|
+
) -> ScenarioList:
|
140
159
|
"""
|
141
160
|
Return a new ScenarioList with a new variable added.
|
142
161
|
|
@@ -145,6 +164,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
145
164
|
>>> s = ScenarioList([Scenario({'a': 1, 'b': 2}), Scenario({'a': 1, 'b': 1})])
|
146
165
|
>>> s.mutate("c = a + b")
|
147
166
|
ScenarioList([Scenario({'a': 1, 'b': 2, 'c': 3}), Scenario({'a': 1, 'b': 1, 'c': 2})])
|
167
|
+
|
148
168
|
"""
|
149
169
|
if "=" not in new_var_string:
|
150
170
|
raise Exception(
|
@@ -222,6 +242,16 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
222
242
|
|
223
243
|
return ScenarioList(new_data)
|
224
244
|
|
245
|
+
def from_urls(self, urls: list[str], field_name: Optional[str] = "text") -> ScenarioList:
|
246
|
+
"""Create a ScenarioList from a list of URLs.
|
247
|
+
|
248
|
+
:param urls: A list of URLs.
|
249
|
+
:param field_name: The name of the field to store the text from the URLs.
|
250
|
+
|
251
|
+
|
252
|
+
"""
|
253
|
+
return ScenarioList([Scenario.from_url(url, field_name) for url in urls])
|
254
|
+
|
225
255
|
def select(self, *fields) -> ScenarioList:
|
226
256
|
"""
|
227
257
|
Selects scenarios with only the references fields.
|
@@ -264,11 +294,19 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
264
294
|
return cls([Scenario({name: value}) for value in values])
|
265
295
|
|
266
296
|
def to_dataset(self) -> "Dataset":
|
297
|
+
"""
|
298
|
+
>>> s = ScenarioList.from_list("a", [1,2,3])
|
299
|
+
>>> s.to_dataset()
|
300
|
+
Dataset([{'a': [1, 2, 3]}])
|
301
|
+
>>> s = ScenarioList.from_list("a", [1,2,3]).add_list("b", [4,5,6])
|
302
|
+
>>> s.to_dataset()
|
303
|
+
Dataset([{'a': [1, 2, 3]}, {'b': [4, 5, 6]}])
|
304
|
+
"""
|
267
305
|
from edsl.results.Dataset import Dataset
|
268
306
|
|
269
307
|
keys = self[0].keys()
|
270
|
-
data = {key: [scenario[key] for scenario in self.data] for key in keys
|
271
|
-
return Dataset(
|
308
|
+
data = [{key: [scenario[key] for scenario in self.data]} for key in keys]
|
309
|
+
return Dataset(data)
|
272
310
|
|
273
311
|
def add_list(self, name, values) -> ScenarioList:
|
274
312
|
"""Add a list of values to a ScenarioList.
|
@@ -286,7 +324,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
286
324
|
self.append(Scenario({name: value}))
|
287
325
|
return self
|
288
326
|
|
289
|
-
def add_value(self, name, value):
|
327
|
+
def add_value(self, name: str, value: Any) -> ScenarioList:
|
290
328
|
"""Add a value to all scenarios in a ScenarioList.
|
291
329
|
|
292
330
|
Example:
|
@@ -340,7 +378,7 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
340
378
|
"""
|
341
379
|
return cls([Scenario(row) for row in df.to_dict(orient="records")])
|
342
380
|
|
343
|
-
def to_key_value(self, field, value=None) -> Union[dict, set]:
|
381
|
+
def to_key_value(self, field: str, value=None) -> Union[dict, set]:
|
344
382
|
"""Return the set of values in the field.
|
345
383
|
|
346
384
|
Example:
|
@@ -459,16 +497,16 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
459
497
|
table.add_row(str(i), s.rich_print())
|
460
498
|
return table
|
461
499
|
|
462
|
-
def print(
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
):
|
469
|
-
|
470
|
-
|
471
|
-
|
500
|
+
# def print(
|
501
|
+
# self,
|
502
|
+
# format: Optional[str] = None,
|
503
|
+
# max_rows: Optional[int] = None,
|
504
|
+
# pretty_labels: Optional[dict] = None,
|
505
|
+
# filename: str = None,
|
506
|
+
# ):
|
507
|
+
# from edsl.utilities.interface import print_scenario_list
|
508
|
+
|
509
|
+
# print_scenario_list(self[:max_rows])
|
472
510
|
|
473
511
|
def __getitem__(self, key: Union[int, slice]) -> Any:
|
474
512
|
"""Return the item at the given index.
|
@@ -5,7 +5,7 @@ from edsl.results.DatasetExportMixin import DatasetExportMixin
|
|
5
5
|
|
6
6
|
|
7
7
|
def to_dataset(func):
|
8
|
-
"""Convert the
|
8
|
+
"""Convert the object to a Dataset object before calling the function."""
|
9
9
|
|
10
10
|
@wraps(func)
|
11
11
|
def wrapper(self, *args, **kwargs):
|
@@ -20,13 +20,24 @@ def to_dataset(func):
|
|
20
20
|
return wrapper
|
21
21
|
|
22
22
|
|
23
|
-
def
|
24
|
-
for attr_name, attr_value in
|
25
|
-
if callable(attr_value):
|
23
|
+
def decorate_methods_from_mixin(cls, mixin_cls):
|
24
|
+
for attr_name, attr_value in mixin_cls.__dict__.items():
|
25
|
+
if callable(attr_value) and not attr_name.startswith("__"):
|
26
26
|
setattr(cls, attr_name, to_dataset(attr_value))
|
27
27
|
return cls
|
28
28
|
|
29
29
|
|
30
|
-
|
30
|
+
# def decorate_all_methods(cls):
|
31
|
+
# for attr_name, attr_value in cls.__dict__.items():
|
32
|
+
# if callable(attr_value):
|
33
|
+
# setattr(cls, attr_name, to_dataset(attr_value))
|
34
|
+
# return cls
|
35
|
+
|
36
|
+
|
37
|
+
# @decorate_all_methods
|
31
38
|
class ScenarioListExportMixin(DatasetExportMixin):
|
32
39
|
"""Mixin class for exporting Results objects."""
|
40
|
+
|
41
|
+
def __init_subclass__(cls, **kwargs):
|
42
|
+
super().__init_subclass__(**kwargs)
|
43
|
+
decorate_methods_from_mixin(cls, DatasetExportMixin)
|
@@ -43,6 +43,9 @@ class ScenarioListPdfMixin:
|
|
43
43
|
|
44
44
|
@staticmethod
|
45
45
|
def extract_text_from_pdf(pdf_path):
|
46
|
+
from edsl import Scenario
|
47
|
+
|
48
|
+
# TODO: Add test case
|
46
49
|
# Ensure the file exists
|
47
50
|
if not os.path.exists(pdf_path):
|
48
51
|
raise FileNotFoundError(f"The file {pdf_path} does not exist.")
|
edsl/study/Study.py
CHANGED
@@ -461,13 +461,13 @@ class Study:
|
|
461
461
|
else:
|
462
462
|
self.objects[oe.hash] = oe
|
463
463
|
|
464
|
-
def push(self
|
464
|
+
def push(self) -> dict:
|
465
465
|
"""Push the objects to coop."""
|
466
466
|
|
467
467
|
from edsl import Coop
|
468
468
|
|
469
469
|
coop = Coop()
|
470
|
-
coop.create(self, description=self.description)
|
470
|
+
return coop.create(self, description=self.description)
|
471
471
|
|
472
472
|
@classmethod
|
473
473
|
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
edsl/surveys/Survey.py
CHANGED
@@ -106,6 +106,39 @@ class Survey(SurveyExportMixin, SurveyFlowVisualizationMixin, Base):
|
|
106
106
|
|
107
107
|
return dict_hash(self._to_dict())
|
108
108
|
|
109
|
+
def __add__(self, other: Survey) -> Survey:
|
110
|
+
"""Combine two surveys.
|
111
|
+
|
112
|
+
:param other: The other survey to combine with this one.
|
113
|
+
>>> s1 = Survey.example()
|
114
|
+
>>> from edsl import QuestionFreeText
|
115
|
+
>>> s2 = Survey([QuestionFreeText(question_text="What is your name?", question_name="yo")])
|
116
|
+
>>> s3 = s1 + s2
|
117
|
+
Traceback (most recent call last):
|
118
|
+
...
|
119
|
+
ValueError: ('Cannot combine two surveys with non-default rules.', "Please use the 'clear_non_default_rules' method to remove non-default rules from the survey.")
|
120
|
+
>>> s3 = s1.clear_non_default_rules() + s2
|
121
|
+
>>> len(s3.questions)
|
122
|
+
4
|
123
|
+
|
124
|
+
"""
|
125
|
+
if (
|
126
|
+
len(self.rule_collection.non_default_rules) > 0
|
127
|
+
or len(other.rule_collection.non_default_rules) > 0
|
128
|
+
):
|
129
|
+
raise ValueError(
|
130
|
+
"Cannot combine two surveys with non-default rules.",
|
131
|
+
"Please use the 'clear_non_default_rules' method to remove non-default rules from the survey.",
|
132
|
+
)
|
133
|
+
|
134
|
+
return Survey(questions=self.questions + other.questions)
|
135
|
+
|
136
|
+
def clear_non_default_rules(self) -> Survey:
|
137
|
+
s = Survey()
|
138
|
+
for question in self.questions:
|
139
|
+
s.add_question(question)
|
140
|
+
return s
|
141
|
+
|
109
142
|
@property
|
110
143
|
def parameters(self):
|
111
144
|
return set.union(*[q.parameters for q in self.questions])
|
@@ -1151,4 +1184,5 @@ def main():
|
|
1151
1184
|
if __name__ == "__main__":
|
1152
1185
|
import doctest
|
1153
1186
|
|
1154
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.SKIP)
|
1187
|
+
# doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.SKIP)
|
1188
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edsl
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.31
|
4
4
|
Summary: Create and analyze LLM-based surveys
|
5
5
|
Home-page: https://www.expectedparrot.com/
|
6
6
|
License: MIT
|
@@ -19,10 +19,11 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
19
|
Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
|
20
20
|
Requires-Dist: anthropic (>=0.23.1,<0.24.0)
|
21
21
|
Requires-Dist: black[jupyter] (>=24.4.2,<25.0.0)
|
22
|
+
Requires-Dist: groq (>=0.9.0,<0.10.0)
|
22
23
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
23
24
|
Requires-Dist: jupyter (>=1.0.0,<2.0.0)
|
24
25
|
Requires-Dist: markdown2 (>=2.4.11,<3.0.0)
|
25
|
-
Requires-Dist: matplotlib (>=3.8
|
26
|
+
Requires-Dist: matplotlib (>=3.8,<3.9)
|
26
27
|
Requires-Dist: nest-asyncio (>=1.5.9,<2.0.0)
|
27
28
|
Requires-Dist: numpy (>=1.22,<2.0)
|
28
29
|
Requires-Dist: openai (>=1.4.0,<2.0.0)
|
@@ -35,6 +36,7 @@ Requires-Dist: python-docx (>=1.1.0,<2.0.0)
|
|
35
36
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
36
37
|
Requires-Dist: restrictedpython (>=7.1,<8.0)
|
37
38
|
Requires-Dist: rich (>=13.7.0,<14.0.0)
|
39
|
+
Requires-Dist: setuptools (<72.0)
|
38
40
|
Requires-Dist: simpleeval (>=0.9.13,<0.10.0)
|
39
41
|
Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
|
40
42
|
Requires-Dist: tenacity (>=8.2.3,<9.0.0)
|