edsl 0.1.33__py3-none-any.whl → 0.1.33.dev1__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/Base.py +3 -9
- edsl/__init__.py +3 -8
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +8 -40
- edsl/agents/AgentList.py +0 -43
- edsl/agents/Invigilator.py +219 -135
- edsl/agents/InvigilatorBase.py +59 -148
- edsl/agents/{PromptConstructor.py → PromptConstructionMixin.py} +89 -138
- edsl/agents/__init__.py +0 -1
- edsl/config.py +56 -47
- edsl/coop/coop.py +7 -50
- edsl/data/Cache.py +1 -35
- edsl/data_transfer_models.py +38 -73
- edsl/enums.py +0 -4
- edsl/exceptions/language_models.py +1 -25
- edsl/exceptions/questions.py +5 -62
- edsl/exceptions/results.py +0 -4
- edsl/inference_services/AnthropicService.py +11 -13
- edsl/inference_services/AwsBedrock.py +17 -19
- edsl/inference_services/AzureAI.py +20 -37
- edsl/inference_services/GoogleService.py +12 -16
- edsl/inference_services/GroqService.py +0 -2
- edsl/inference_services/InferenceServiceABC.py +3 -58
- edsl/inference_services/OpenAIService.py +54 -48
- edsl/inference_services/models_available_cache.py +6 -0
- edsl/inference_services/registry.py +0 -6
- edsl/jobs/Answers.py +12 -10
- edsl/jobs/Jobs.py +21 -36
- edsl/jobs/buckets/BucketCollection.py +15 -24
- edsl/jobs/buckets/TokenBucket.py +14 -93
- edsl/jobs/interviews/Interview.py +78 -366
- edsl/jobs/interviews/InterviewExceptionEntry.py +19 -85
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +286 -0
- edsl/jobs/interviews/{InterviewExceptionCollection.py → interview_exception_tracking.py} +68 -14
- edsl/jobs/interviews/retry_management.py +37 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +175 -146
- edsl/jobs/runners/JobsRunnerStatusMixin.py +333 -0
- edsl/jobs/tasks/QuestionTaskCreator.py +23 -30
- edsl/jobs/tasks/TaskHistory.py +213 -148
- edsl/language_models/LanguageModel.py +156 -261
- edsl/language_models/ModelList.py +2 -2
- edsl/language_models/RegisterLanguageModelsMeta.py +29 -14
- edsl/language_models/registry.py +6 -23
- edsl/language_models/repair.py +19 -0
- edsl/prompts/Prompt.py +2 -52
- edsl/questions/AnswerValidatorMixin.py +26 -23
- edsl/questions/QuestionBase.py +249 -329
- edsl/questions/QuestionBudget.py +41 -99
- edsl/questions/QuestionCheckBox.py +35 -227
- edsl/questions/QuestionExtract.py +27 -98
- edsl/questions/QuestionFreeText.py +29 -52
- edsl/questions/QuestionFunctional.py +0 -7
- edsl/questions/QuestionList.py +22 -141
- edsl/questions/QuestionMultipleChoice.py +65 -159
- edsl/questions/QuestionNumerical.py +46 -88
- edsl/questions/QuestionRank.py +24 -182
- edsl/questions/RegisterQuestionsMeta.py +12 -31
- edsl/questions/__init__.py +4 -3
- edsl/questions/derived/QuestionLikertFive.py +5 -10
- edsl/questions/derived/QuestionLinearScale.py +2 -15
- edsl/questions/derived/QuestionTopK.py +1 -10
- edsl/questions/derived/QuestionYesNo.py +3 -24
- edsl/questions/descriptors.py +7 -43
- edsl/questions/question_registry.py +2 -6
- edsl/results/Dataset.py +0 -20
- edsl/results/DatasetExportMixin.py +48 -46
- edsl/results/Result.py +5 -32
- edsl/results/Results.py +46 -135
- edsl/results/ResultsDBMixin.py +3 -3
- edsl/scenarios/FileStore.py +10 -71
- edsl/scenarios/Scenario.py +25 -96
- edsl/scenarios/ScenarioImageMixin.py +2 -2
- edsl/scenarios/ScenarioList.py +39 -361
- edsl/scenarios/ScenarioListExportMixin.py +0 -9
- edsl/scenarios/ScenarioListPdfMixin.py +4 -150
- edsl/study/SnapShot.py +1 -8
- edsl/study/Study.py +0 -32
- edsl/surveys/Rule.py +1 -10
- edsl/surveys/RuleCollection.py +5 -21
- edsl/surveys/Survey.py +310 -636
- edsl/surveys/SurveyExportMixin.py +9 -71
- edsl/surveys/SurveyFlowVisualizationMixin.py +1 -2
- edsl/surveys/SurveyQualtricsImport.py +4 -75
- edsl/utilities/gcp_bucket/simple_example.py +9 -0
- edsl/utilities/utilities.py +1 -9
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/METADATA +2 -5
- edsl-0.1.33.dev1.dist-info/RECORD +209 -0
- edsl/TemplateLoader.py +0 -24
- edsl/auto/AutoStudy.py +0 -117
- edsl/auto/StageBase.py +0 -230
- edsl/auto/StageGenerateSurvey.py +0 -178
- edsl/auto/StageLabelQuestions.py +0 -125
- edsl/auto/StagePersona.py +0 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +0 -88
- edsl/auto/StagePersonaDimensionValues.py +0 -74
- edsl/auto/StagePersonaDimensions.py +0 -69
- edsl/auto/StageQuestions.py +0 -73
- edsl/auto/SurveyCreatorPipeline.py +0 -21
- edsl/auto/utilities.py +0 -224
- edsl/coop/PriceFetcher.py +0 -58
- edsl/inference_services/MistralAIService.py +0 -120
- edsl/inference_services/TestService.py +0 -80
- edsl/inference_services/TogetherAIService.py +0 -170
- edsl/jobs/FailedQuestion.py +0 -78
- edsl/jobs/runners/JobsRunnerStatus.py +0 -331
- edsl/language_models/fake_openai_call.py +0 -15
- edsl/language_models/fake_openai_service.py +0 -61
- edsl/language_models/utilities.py +0 -61
- edsl/questions/QuestionBaseGenMixin.py +0 -133
- edsl/questions/QuestionBasePromptsMixin.py +0 -266
- edsl/questions/Quick.py +0 -41
- edsl/questions/ResponseValidatorABC.py +0 -170
- edsl/questions/decorators.py +0 -21
- edsl/questions/prompt_templates/question_budget.jinja +0 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +0 -32
- edsl/questions/prompt_templates/question_extract.jinja +0 -11
- edsl/questions/prompt_templates/question_free_text.jinja +0 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +0 -11
- edsl/questions/prompt_templates/question_list.jinja +0 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +0 -33
- edsl/questions/prompt_templates/question_numerical.jinja +0 -37
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +0 -7
- edsl/questions/templates/budget/question_presentation.jinja +0 -7
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +0 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +0 -22
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/extract/answering_instructions.jinja +0 -7
- edsl/questions/templates/extract/question_presentation.jinja +0 -1
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +0 -1
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +0 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +0 -12
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +0 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +0 -5
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +0 -4
- edsl/questions/templates/list/question_presentation.jinja +0 -5
- edsl/questions/templates/multiple_choice/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +0 -9
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +0 -12
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +0 -8
- edsl/questions/templates/numerical/question_presentation.jinja +0 -7
- edsl/questions/templates/rank/__init__.py +0 -0
- edsl/questions/templates/rank/answering_instructions.jinja +0 -11
- edsl/questions/templates/rank/question_presentation.jinja +0 -15
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +0 -8
- edsl/questions/templates/top_k/question_presentation.jinja +0 -22
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +0 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +0 -12
- edsl/results/DatasetTree.py +0 -145
- edsl/results/Selector.py +0 -118
- edsl/results/tree_explore.py +0 -115
- edsl/surveys/instructions/ChangeInstruction.py +0 -47
- edsl/surveys/instructions/Instruction.py +0 -34
- edsl/surveys/instructions/InstructionCollection.py +0 -77
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +0 -24
- edsl/templates/error_reporting/exceptions_by_model.html +0 -35
- edsl/templates/error_reporting/exceptions_by_question_name.html +0 -17
- edsl/templates/error_reporting/exceptions_by_type.html +0 -17
- edsl/templates/error_reporting/interview_details.html +0 -116
- edsl/templates/error_reporting/interviews.html +0 -10
- edsl/templates/error_reporting/overview.html +0 -5
- edsl/templates/error_reporting/performance_plot.html +0 -2
- edsl/templates/error_reporting/report.css +0 -74
- edsl/templates/error_reporting/report.html +0 -118
- edsl/templates/error_reporting/report.js +0 -25
- edsl-0.1.33.dist-info/RECORD +0 -295
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/LICENSE +0 -0
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev1.dist-info}/WHEEL +0 -0
@@ -3,7 +3,6 @@ from typing import Optional
|
|
3
3
|
|
4
4
|
from edsl.exceptions import QuestionCreationValidationError
|
5
5
|
from edsl.questions.QuestionCheckBox import QuestionCheckBox
|
6
|
-
from edsl.questions.decorators import inject_exception
|
7
6
|
|
8
7
|
|
9
8
|
class QuestionTopK(QuestionCheckBox):
|
@@ -18,9 +17,6 @@ class QuestionTopK(QuestionCheckBox):
|
|
18
17
|
question_options: list[str],
|
19
18
|
min_selections: int,
|
20
19
|
max_selections: int,
|
21
|
-
question_presentation: Optional[str] = None,
|
22
|
-
answering_instructions: Optional[str] = None,
|
23
|
-
include_comment: Optional[bool] = True,
|
24
20
|
):
|
25
21
|
"""Initialize the question.
|
26
22
|
|
@@ -36,9 +32,6 @@ class QuestionTopK(QuestionCheckBox):
|
|
36
32
|
question_options=question_options,
|
37
33
|
min_selections=min_selections,
|
38
34
|
max_selections=max_selections,
|
39
|
-
question_presentation=question_presentation,
|
40
|
-
answering_instructions=answering_instructions,
|
41
|
-
include_comment=include_comment,
|
42
35
|
)
|
43
36
|
if min_selections != max_selections:
|
44
37
|
raise QuestionCreationValidationError(
|
@@ -53,8 +46,7 @@ class QuestionTopK(QuestionCheckBox):
|
|
53
46
|
# Helpful
|
54
47
|
################
|
55
48
|
@classmethod
|
56
|
-
|
57
|
-
def example(cls, include_comment: bool = True) -> QuestionTopK:
|
49
|
+
def example(cls) -> QuestionTopK:
|
58
50
|
"""Return an example question."""
|
59
51
|
return cls(
|
60
52
|
question_name="two_fruits",
|
@@ -62,7 +54,6 @@ class QuestionTopK(QuestionCheckBox):
|
|
62
54
|
question_options=["apple", "banana", "carrot", "durian"],
|
63
55
|
min_selections=2,
|
64
56
|
max_selections=2,
|
65
|
-
include_comment=include_comment,
|
66
57
|
)
|
67
58
|
|
68
59
|
|
@@ -1,10 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Optional
|
3
2
|
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
4
3
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
5
4
|
|
6
|
-
from edsl.questions.decorators import inject_exception
|
7
|
-
|
8
5
|
|
9
6
|
class QuestionYesNo(QuestionMultipleChoice):
|
10
7
|
"""This question prompts the agent to respond with 'Yes' or 'No'."""
|
@@ -16,10 +13,7 @@ class QuestionYesNo(QuestionMultipleChoice):
|
|
16
13
|
self,
|
17
14
|
question_name: str,
|
18
15
|
question_text: str,
|
19
|
-
question_options: list[str] = ["
|
20
|
-
answering_instructions: Optional[str] = None,
|
21
|
-
question_presentation: Optional[str] = None,
|
22
|
-
include_comment: Optional[bool] = True,
|
16
|
+
question_options: list[str] = ["Yes", "No"],
|
23
17
|
):
|
24
18
|
"""Instantiate a new QuestionYesNo.
|
25
19
|
|
@@ -31,10 +25,6 @@ class QuestionYesNo(QuestionMultipleChoice):
|
|
31
25
|
question_name=question_name,
|
32
26
|
question_text=question_text,
|
33
27
|
question_options=question_options,
|
34
|
-
use_code=False,
|
35
|
-
answering_instructions=answering_instructions,
|
36
|
-
question_presentation=question_presentation,
|
37
|
-
include_comment=include_comment,
|
38
28
|
)
|
39
29
|
self.question_options = question_options
|
40
30
|
|
@@ -42,14 +32,9 @@ class QuestionYesNo(QuestionMultipleChoice):
|
|
42
32
|
# Helpful
|
43
33
|
################
|
44
34
|
@classmethod
|
45
|
-
|
46
|
-
def example(cls, include_comment: bool = True) -> QuestionYesNo:
|
35
|
+
def example(cls) -> QuestionYesNo:
|
47
36
|
"""Return an example of a yes/no question."""
|
48
|
-
return cls(
|
49
|
-
question_name="is_it_equal",
|
50
|
-
question_text="Is 5 + 5 equal to 11?",
|
51
|
-
include_comment=include_comment,
|
52
|
-
)
|
37
|
+
return cls(question_name="is_it_equal", question_text="Is 5 + 5 equal to 11?")
|
53
38
|
|
54
39
|
|
55
40
|
def main():
|
@@ -74,9 +59,3 @@ def main():
|
|
74
59
|
import doctest
|
75
60
|
|
76
61
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
77
|
-
|
78
|
-
|
79
|
-
if __name__ == "__main__":
|
80
|
-
import doctest
|
81
|
-
|
82
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/questions/descriptors.py
CHANGED
@@ -206,14 +206,12 @@ class OptionLabelDescriptor(BaseDescriptor):
|
|
206
206
|
|
207
207
|
def validate(self, value, instance):
|
208
208
|
"""Validate the value is a string."""
|
209
|
-
|
210
|
-
|
211
|
-
if value and (key_values := [float(v) for v in value.keys()]) != []:
|
212
|
-
if min(key_values) != min(instance.question_options):
|
209
|
+
if value is not None:
|
210
|
+
if min(value.keys()) != min(instance.question_options):
|
213
211
|
raise QuestionCreationValidationError(
|
214
212
|
f"First option needs a label (got {value})"
|
215
213
|
)
|
216
|
-
if max(
|
214
|
+
if max(value.keys()) != max(instance.question_options):
|
217
215
|
raise QuestionCreationValidationError(
|
218
216
|
f"Last option needs a label (got {value})"
|
219
217
|
)
|
@@ -221,17 +219,12 @@ class OptionLabelDescriptor(BaseDescriptor):
|
|
221
219
|
raise QuestionCreationValidationError(
|
222
220
|
"Option labels must be strings (got {value})."
|
223
221
|
)
|
224
|
-
for key in
|
222
|
+
for key in value.keys():
|
225
223
|
if key not in instance.question_options:
|
226
224
|
raise QuestionCreationValidationError(
|
227
225
|
f"Option label key ({key}) is not in question options ({instance.question_options})."
|
228
226
|
)
|
229
227
|
|
230
|
-
if len(value.values()) != len(set(value.values())):
|
231
|
-
raise QuestionCreationValidationError(
|
232
|
-
f"Option labels must be unique (got {value})."
|
233
|
-
)
|
234
|
-
|
235
228
|
|
236
229
|
class QuestionNameDescriptor(BaseDescriptor):
|
237
230
|
"""Validate that the `question_name` attribute is a valid variable name."""
|
@@ -240,15 +233,6 @@ class QuestionNameDescriptor(BaseDescriptor):
|
|
240
233
|
"""Validate the value is a valid variable name."""
|
241
234
|
from edsl.utilities.utilities import is_valid_variable_name
|
242
235
|
|
243
|
-
if "{{" in value and "}}" in value:
|
244
|
-
# they're trying to use a dynamic question name - let's let this play out
|
245
|
-
return None
|
246
|
-
|
247
|
-
if value.endswith("_comment") or value.endswith("_generated_tokens"):
|
248
|
-
raise QuestionCreationValidationError(
|
249
|
-
f"`question_name` cannot end with '_comment' or '_generated_tokens - (got {value})."
|
250
|
-
)
|
251
|
-
|
252
236
|
if not is_valid_variable_name(value):
|
253
237
|
raise QuestionCreationValidationError(
|
254
238
|
f"`question_name` is not a valid variable name (got {value})."
|
@@ -295,7 +279,7 @@ class QuestionOptionsDescriptor(BaseDescriptor):
|
|
295
279
|
>>> _ = q_class("dynamic_options")
|
296
280
|
Traceback (most recent call last):
|
297
281
|
...
|
298
|
-
edsl.exceptions.questions.QuestionCreationValidationError:
|
282
|
+
edsl.exceptions.questions.QuestionCreationValidationError: Dynamic question options must be of the form: '{{ question_options }}'.
|
299
283
|
"""
|
300
284
|
if isinstance(value, str):
|
301
285
|
# Check if the string is a dynamic question option
|
@@ -303,7 +287,7 @@ class QuestionOptionsDescriptor(BaseDescriptor):
|
|
303
287
|
return None
|
304
288
|
else:
|
305
289
|
raise QuestionCreationValidationError(
|
306
|
-
|
290
|
+
"Dynamic question options must be of the form: '{{ question_options }}'."
|
307
291
|
)
|
308
292
|
if not isinstance(value, list):
|
309
293
|
raise QuestionCreationValidationError(
|
@@ -372,21 +356,7 @@ class QuestionOptionsDescriptor(BaseDescriptor):
|
|
372
356
|
|
373
357
|
|
374
358
|
class QuestionTextDescriptor(BaseDescriptor):
|
375
|
-
"""Validate that the `question_text` attribute is a string.
|
376
|
-
|
377
|
-
|
378
|
-
>>> class TestQuestion:
|
379
|
-
... question_text = QuestionTextDescriptor()
|
380
|
-
... def __init__(self, question_text: str):
|
381
|
-
... self.question_text = question_text
|
382
|
-
|
383
|
-
>>> _ = TestQuestion("What is the capital of France?")
|
384
|
-
>>> _ = TestQuestion("What is the capital of France? {{variable}}")
|
385
|
-
>>> _ = TestQuestion("What is the capital of France? {{variable name}}")
|
386
|
-
Traceback (most recent call last):
|
387
|
-
...
|
388
|
-
edsl.exceptions.questions.QuestionCreationValidationError: Question text contains an invalid identifier: 'variable name'
|
389
|
-
"""
|
359
|
+
"""Validate that the `question_text` attribute is a string."""
|
390
360
|
|
391
361
|
def validate(self, value, instance):
|
392
362
|
"""Validate the value is a string."""
|
@@ -403,12 +373,6 @@ class QuestionTextDescriptor(BaseDescriptor):
|
|
403
373
|
f"WARNING: Question text contains a single-braced substring: If you intended to parameterize the question with a Scenario this should be changed to a double-braced substring, e.g. {{variable}}.\nSee details on constructing Scenarios in the docs: https://docs.expectedparrot.com/en/latest/scenarios.html",
|
404
374
|
UserWarning,
|
405
375
|
)
|
406
|
-
# iterate through all doubles braces and check if they are valid python identifiers
|
407
|
-
for match in re.finditer(r"\{\{([^\{\}]+)\}\}", value):
|
408
|
-
if " " in match.group(1).strip():
|
409
|
-
raise QuestionCreationValidationError(
|
410
|
-
f"Question text contains an invalid identifier: '{match.group(1)}'"
|
411
|
-
)
|
412
376
|
|
413
377
|
|
414
378
|
if __name__ == "__main__":
|
@@ -100,16 +100,12 @@ class Question(metaclass=Meta):
|
|
100
100
|
|
101
101
|
>>> from edsl import Question
|
102
102
|
>>> Question.available()
|
103
|
-
['checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
|
103
|
+
['budget', 'checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
|
104
104
|
"""
|
105
|
-
exclude = ["budget"]
|
106
105
|
if show_class_names:
|
107
106
|
return RegisterQuestionsMeta.question_types_to_classes()
|
108
107
|
else:
|
109
|
-
|
110
|
-
set(RegisterQuestionsMeta.question_types_to_classes().keys())
|
111
|
-
)
|
112
|
-
return [q for q in question_list if q not in exclude]
|
108
|
+
return sorted(set(RegisterQuestionsMeta.question_types_to_classes().keys()))
|
113
109
|
|
114
110
|
|
115
111
|
def get_question_class(question_type):
|
edsl/results/Dataset.py
CHANGED
@@ -8,7 +8,6 @@ from typing import Any, Union, Optional
|
|
8
8
|
import numpy as np
|
9
9
|
|
10
10
|
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
11
|
-
from edsl.results.DatasetTree import Tree
|
12
11
|
|
13
12
|
|
14
13
|
class Dataset(UserList, ResultsExportMixin):
|
@@ -31,15 +30,6 @@ class Dataset(UserList, ResultsExportMixin):
|
|
31
30
|
_, values = list(self.data[0].items())[0]
|
32
31
|
return len(values)
|
33
32
|
|
34
|
-
def keys(self):
|
35
|
-
"""Return the keys of the first observation in the dataset.
|
36
|
-
|
37
|
-
>>> d = Dataset([{'a.b':[1,2,3,4]}])
|
38
|
-
>>> d.keys()
|
39
|
-
['a.b']
|
40
|
-
"""
|
41
|
-
return [list(o.keys())[0] for o in self]
|
42
|
-
|
43
33
|
def __repr__(self) -> str:
|
44
34
|
"""Return a string representation of the dataset."""
|
45
35
|
return f"Dataset({self.data})"
|
@@ -255,16 +245,6 @@ class Dataset(UserList, ResultsExportMixin):
|
|
255
245
|
|
256
246
|
return Dataset(new_data)
|
257
247
|
|
258
|
-
@property
|
259
|
-
def tree(self):
|
260
|
-
"""Return a tree representation of the dataset.
|
261
|
-
|
262
|
-
>>> d = Dataset([{'a':[1,2,3,4]}, {'b':[4,3,2,1]}])
|
263
|
-
>>> d.tree.print_tree()
|
264
|
-
Tree has not been constructed yet.
|
265
|
-
"""
|
266
|
-
return Tree(self)
|
267
|
-
|
268
248
|
@classmethod
|
269
249
|
def example(self):
|
270
250
|
"""Return an example dataset.
|
@@ -4,7 +4,6 @@ import base64
|
|
4
4
|
import csv
|
5
5
|
import io
|
6
6
|
import html
|
7
|
-
from typing import Optional
|
8
7
|
|
9
8
|
from typing import Literal, Optional, Union, List
|
10
9
|
|
@@ -42,7 +41,7 @@ class DatasetExportMixin:
|
|
42
41
|
>>> Results.example().relevant_columns(data_type = "flimflam")
|
43
42
|
Traceback (most recent call last):
|
44
43
|
...
|
45
|
-
ValueError: No columns found for data type: flimflam. Available data types are:
|
44
|
+
ValueError: No columns found for data type: flimflam. Available data types are: ['agent', 'answer', 'comment', 'model', 'prompt', 'question_options', 'question_text', 'question_type', 'raw_model_response', 'scenario'].
|
46
45
|
"""
|
47
46
|
columns = [list(x.keys())[0] for x in self]
|
48
47
|
if remove_prefix:
|
@@ -157,13 +156,12 @@ class DatasetExportMixin:
|
|
157
156
|
iframe_height: int = 200,
|
158
157
|
iframe_width: int = 600,
|
159
158
|
web=False,
|
160
|
-
|
161
|
-
) -> Union[None, str, "Results"]:
|
159
|
+
) -> None:
|
162
160
|
"""Print the results in a pretty format.
|
163
161
|
|
164
162
|
:param pretty_labels: A dictionary of pretty labels for the columns.
|
165
163
|
:param filename: The filename to save the results to.
|
166
|
-
:param format: The format to print the results in. Options are 'rich', 'html',
|
164
|
+
:param format: The format to print the results in. Options are 'rich', 'html', or 'markdown'.
|
167
165
|
:param interactive: Whether to print the results interactively in a Jupyter notebook.
|
168
166
|
:param split_at_dot: Whether to split the column names at the last dot w/ a newline.
|
169
167
|
:param max_rows: The maximum number of rows to print.
|
@@ -172,9 +170,6 @@ class DatasetExportMixin:
|
|
172
170
|
:param iframe_height: The height of the iframe.
|
173
171
|
:param iframe_width: The width of the iframe.
|
174
172
|
:param web: Whether to display the table in a web browser.
|
175
|
-
:param return_string: Whether to return the output as a string instead of printing.
|
176
|
-
|
177
|
-
:return: None if tee is False and return_string is False, the dataset if tee is True, or a string if return_string is True.
|
178
173
|
|
179
174
|
Example: Print in rich format at the terminal
|
180
175
|
|
@@ -258,14 +253,11 @@ class DatasetExportMixin:
|
|
258
253
|
|
259
254
|
>>> r.select('how_feeling').print(format='latex')
|
260
255
|
\\begin{tabular}{l}
|
256
|
+
\\toprule
|
261
257
|
...
|
262
|
-
\\end{tabular}
|
263
|
-
<BLANKLINE>
|
264
258
|
"""
|
265
259
|
from IPython.display import HTML, display
|
266
260
|
from edsl.utilities.utilities import is_notebook
|
267
|
-
import io
|
268
|
-
import sys
|
269
261
|
|
270
262
|
def _determine_format(format):
|
271
263
|
if format is None:
|
@@ -274,9 +266,7 @@ class DatasetExportMixin:
|
|
274
266
|
else:
|
275
267
|
format = "rich"
|
276
268
|
if format not in ["rich", "html", "markdown", "latex"]:
|
277
|
-
raise ValueError(
|
278
|
-
"format must be one of 'rich', 'html', 'markdown', or 'latex'."
|
279
|
-
)
|
269
|
+
raise ValueError("format must be one of 'rich', 'html', or 'markdown'.")
|
280
270
|
|
281
271
|
return format
|
282
272
|
|
@@ -295,24 +285,21 @@ class DatasetExportMixin:
|
|
295
285
|
|
296
286
|
new_data = list(_create_data())
|
297
287
|
|
298
|
-
# Capture output if return_string is True
|
299
|
-
if return_string:
|
300
|
-
old_stdout = sys.stdout
|
301
|
-
sys.stdout = io.StringIO()
|
302
|
-
|
303
|
-
output = None
|
304
|
-
|
305
288
|
if format == "rich":
|
306
289
|
from edsl.utilities.interface import print_dataset_with_rich
|
307
290
|
|
308
|
-
|
291
|
+
print_dataset_with_rich(
|
309
292
|
new_data, filename=filename, split_at_dot=split_at_dot
|
310
293
|
)
|
311
|
-
|
294
|
+
return self if tee else None
|
295
|
+
|
296
|
+
if format == "markdown":
|
312
297
|
from edsl.utilities.interface import print_list_of_dicts_as_markdown_table
|
313
298
|
|
314
|
-
|
315
|
-
|
299
|
+
print_list_of_dicts_as_markdown_table(new_data, filename=filename)
|
300
|
+
return self if tee else None
|
301
|
+
|
302
|
+
if format == "latex":
|
316
303
|
df = self.to_pandas()
|
317
304
|
df.columns = [col.replace("_", " ") for col in df.columns]
|
318
305
|
latex_string = df.to_latex(index=False)
|
@@ -322,14 +309,23 @@ class DatasetExportMixin:
|
|
322
309
|
f.write(latex_string)
|
323
310
|
else:
|
324
311
|
print(latex_string)
|
325
|
-
|
326
|
-
|
312
|
+
|
313
|
+
return self if tee else None
|
314
|
+
|
315
|
+
if format == "html":
|
327
316
|
from edsl.utilities.interface import print_list_of_dicts_as_html_table
|
328
317
|
|
329
318
|
html_source = print_list_of_dicts_as_html_table(
|
330
319
|
new_data, interactive=interactive
|
331
320
|
)
|
332
321
|
|
322
|
+
# if download_link:
|
323
|
+
# from IPython.display import HTML, display
|
324
|
+
# csv_file = output.getvalue()
|
325
|
+
# b64 = base64.b64encode(csv_file.encode()).decode()
|
326
|
+
# download_link = f'<a href="data:file/csv;base64,{b64}" download="my_data.csv">Download CSV file</a>'
|
327
|
+
# #display(HTML(download_link))
|
328
|
+
|
333
329
|
if iframe:
|
334
330
|
iframe = f""""
|
335
331
|
<iframe srcdoc="{ html.escape(html_source) }" style="width: {iframe_width}px; height: {iframe_height}px;"></iframe>
|
@@ -342,18 +338,7 @@ class DatasetExportMixin:
|
|
342
338
|
|
343
339
|
view_html(html_source)
|
344
340
|
|
345
|
-
|
346
|
-
|
347
|
-
# Restore stdout and get captured output if return_string is True
|
348
|
-
if return_string:
|
349
|
-
captured_output = sys.stdout.getvalue()
|
350
|
-
sys.stdout = old_stdout
|
351
|
-
return captured_output or output
|
352
|
-
|
353
|
-
if tee:
|
354
|
-
return self
|
355
|
-
|
356
|
-
return None
|
341
|
+
return self if tee else None
|
357
342
|
|
358
343
|
def to_csv(
|
359
344
|
self,
|
@@ -472,11 +457,7 @@ class DatasetExportMixin:
|
|
472
457
|
from edsl import ScenarioList, Scenario
|
473
458
|
|
474
459
|
list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
|
475
|
-
|
476
|
-
for d in list_of_dicts:
|
477
|
-
scenarios.append(Scenario(d))
|
478
|
-
return ScenarioList(scenarios)
|
479
|
-
# return ScenarioList([Scenario(d) for d in list_of_dicts])
|
460
|
+
return ScenarioList([Scenario(d) for d in list_of_dicts])
|
480
461
|
|
481
462
|
def to_agent_list(self, remove_prefix: bool = True):
|
482
463
|
"""Convert the results to a list of dictionaries, one per agent.
|
@@ -520,7 +501,7 @@ class DatasetExportMixin:
|
|
520
501
|
|
521
502
|
return list_of_dicts
|
522
503
|
|
523
|
-
def to_list(self, flatten=False, remove_none=False
|
504
|
+
def to_list(self, flatten=False, remove_none=False) -> list[list]:
|
524
505
|
"""Convert the results to a list of lists.
|
525
506
|
|
526
507
|
:param flatten: Whether to flatten the list of lists.
|
@@ -615,6 +596,27 @@ class DatasetExportMixin:
|
|
615
596
|
if return_link:
|
616
597
|
return filename
|
617
598
|
|
599
|
+
def to_docx(self, filename: Optional[str] = None, separator: str = "\n"):
|
600
|
+
"""Export the results to a Word document.
|
601
|
+
|
602
|
+
:param filename: The filename to save the Word document to.
|
603
|
+
|
604
|
+
|
605
|
+
"""
|
606
|
+
from docx import Document
|
607
|
+
|
608
|
+
doc = Document()
|
609
|
+
for entry in self:
|
610
|
+
key, values = list(entry.items())[0]
|
611
|
+
doc.add_paragraph(key)
|
612
|
+
line = separator.join(values)
|
613
|
+
doc.add_paragraph(line)
|
614
|
+
|
615
|
+
if filename is not None:
|
616
|
+
doc.save(filename)
|
617
|
+
else:
|
618
|
+
return doc
|
619
|
+
|
618
620
|
def tally(
|
619
621
|
self, *fields: Optional[str], top_n: Optional[int] = None, output="Dataset"
|
620
622
|
) -> Union[dict, "Dataset"]:
|
edsl/results/Result.py
CHANGED
@@ -53,8 +53,8 @@ class Result(Base, UserDict):
|
|
53
53
|
|
54
54
|
>>> import warnings
|
55
55
|
>>> warnings.simplefilter("ignore", UserWarning)
|
56
|
-
>>> Result.example().answer
|
57
|
-
|
56
|
+
>>> Result.example().answer
|
57
|
+
{'how_feeling': 'OK', 'how_feeling_comment': 'This is a real survey response from a human.', 'how_feeling_yesterday': 'Great', 'how_feeling_yesterday_comment': 'This is a real survey response from a human.'}
|
58
58
|
|
59
59
|
Its main data is an Agent, a Scenario, a Model, an Iteration, and an Answer.
|
60
60
|
These are stored both in the UserDict and as attributes.
|
@@ -73,8 +73,6 @@ class Result(Base, UserDict):
|
|
73
73
|
raw_model_response=None,
|
74
74
|
survey: Optional["Survey"] = None,
|
75
75
|
question_to_attributes: Optional[dict] = None,
|
76
|
-
generated_tokens: Optional[dict] = None,
|
77
|
-
comments_dict: Optional[dict] = None,
|
78
76
|
):
|
79
77
|
"""Initialize a Result object.
|
80
78
|
|
@@ -115,7 +113,6 @@ class Result(Base, UserDict):
|
|
115
113
|
"prompt": prompt or {},
|
116
114
|
"raw_model_response": raw_model_response or {},
|
117
115
|
"question_to_attributes": question_to_attributes,
|
118
|
-
"generated_tokens": generated_tokens or {},
|
119
116
|
}
|
120
117
|
super().__init__(**data)
|
121
118
|
# but also store the data as attributes
|
@@ -128,8 +125,6 @@ class Result(Base, UserDict):
|
|
128
125
|
self.raw_model_response = raw_model_response or {}
|
129
126
|
self.survey = survey
|
130
127
|
self.question_to_attributes = question_to_attributes
|
131
|
-
self.generated_tokens = generated_tokens
|
132
|
-
self.comments_dict = comments_dict or {}
|
133
128
|
|
134
129
|
self._combined_dict = None
|
135
130
|
self._problem_keys = None
|
@@ -145,7 +140,7 @@ class Result(Base, UserDict):
|
|
145
140
|
else:
|
146
141
|
agent_name = self.agent.name
|
147
142
|
|
148
|
-
|
143
|
+
comments_dict = {k: v for k, v in self.answer.items() if k.endswith("_comment")}
|
149
144
|
question_text_dict = {}
|
150
145
|
question_options_dict = {}
|
151
146
|
question_type_dict = {}
|
@@ -172,12 +167,11 @@ class Result(Base, UserDict):
|
|
172
167
|
"answer": self.answer,
|
173
168
|
"prompt": self.prompt,
|
174
169
|
"raw_model_response": self.raw_model_response,
|
175
|
-
"iteration": {"iteration": self.iteration},
|
170
|
+
# "iteration": {"iteration": self.iteration},
|
176
171
|
"question_text": question_text_dict,
|
177
172
|
"question_options": question_options_dict,
|
178
173
|
"question_type": question_type_dict,
|
179
|
-
"comment":
|
180
|
-
"generated_tokens": self.generated_tokens,
|
174
|
+
"comment": comments_dict,
|
181
175
|
}
|
182
176
|
|
183
177
|
def check_expression(self, expression) -> None:
|
@@ -266,26 +260,6 @@ class Result(Base, UserDict):
|
|
266
260
|
for key, value in subdict.items():
|
267
261
|
yield (index, data_type, key, str(value))
|
268
262
|
|
269
|
-
def leaves(self):
|
270
|
-
leaves = []
|
271
|
-
for question_name, answer in self.answer.items():
|
272
|
-
if not question_name.endswith("_comment"):
|
273
|
-
leaves.append(
|
274
|
-
{
|
275
|
-
"question": f"({question_name}): "
|
276
|
-
+ str(
|
277
|
-
self.question_to_attributes[question_name]["question_text"]
|
278
|
-
),
|
279
|
-
"answer": answer,
|
280
|
-
"comment": self.answer.get(question_name + "_comment", ""),
|
281
|
-
"scenario": repr(self.scenario),
|
282
|
-
"agent": repr(self.agent),
|
283
|
-
"model": repr(self.model),
|
284
|
-
"iteration": self.iteration,
|
285
|
-
}
|
286
|
-
)
|
287
|
-
return leaves
|
288
|
-
|
289
263
|
###############
|
290
264
|
# Useful
|
291
265
|
###############
|
@@ -367,7 +341,6 @@ class Result(Base, UserDict):
|
|
367
341
|
"raw_model_response", {"raw_model_response": "No raw model response"}
|
368
342
|
),
|
369
343
|
question_to_attributes=json_dict.get("question_to_attributes", None),
|
370
|
-
generated_tokens=json_dict.get("generated_tokens", {}),
|
371
344
|
)
|
372
345
|
return result
|
373
346
|
|