edsl 0.1.28__py3-none-any.whl → 0.1.29__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 +18 -18
- edsl/__init__.py +24 -24
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +77 -41
- edsl/agents/AgentList.py +35 -6
- edsl/agents/Invigilator.py +19 -1
- edsl/agents/InvigilatorBase.py +15 -10
- edsl/agents/PromptConstructionMixin.py +342 -100
- edsl/agents/descriptors.py +2 -1
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/conjure/InputData.py +39 -8
- edsl/coop/coop.py +188 -151
- edsl/coop/utils.py +43 -75
- edsl/data/Cache.py +19 -5
- edsl/data/SQLiteDict.py +11 -3
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +92 -47
- edsl/jobs/buckets/ModelBuckets.py +4 -2
- edsl/jobs/buckets/TokenBucket.py +1 -2
- edsl/jobs/interviews/Interview.py +3 -9
- edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +15 -10
- edsl/jobs/runners/JobsRunnerAsyncio.py +21 -25
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +5 -11
- edsl/language_models/ModelList.py +3 -3
- edsl/language_models/repair.py +8 -7
- edsl/notebooks/Notebook.py +40 -3
- edsl/prompts/Prompt.py +31 -19
- edsl/questions/QuestionBase.py +38 -13
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +3 -3
- edsl/questions/QuestionFunctional.py +0 -3
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +16 -8
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +4 -2
- edsl/questions/question_registry.py +20 -31
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +31 -0
- edsl/results/DatasetExportMixin.py +493 -0
- edsl/results/Result.py +22 -74
- edsl/results/Results.py +105 -67
- edsl/results/ResultsDBMixin.py +7 -3
- edsl/results/ResultsExportMixin.py +22 -537
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +5 -5
- edsl/scenarios/FileStore.py +140 -0
- edsl/scenarios/Scenario.py +5 -6
- edsl/scenarios/ScenarioList.py +44 -15
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/study/ObjectEntry.py +89 -13
- edsl/study/ProofOfWork.py +5 -2
- edsl/study/SnapShot.py +4 -8
- edsl/study/Study.py +21 -14
- edsl/study/__init__.py +2 -0
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +46 -7
- edsl/surveys/SurveyExportMixin.py +4 -2
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/tools/plotting.py +4 -2
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +66 -45
- edsl/utilities/utilities.py +11 -13
- {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/METADATA +11 -10
- {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/RECORD +75 -72
- edsl-0.1.28.dist-info/entry_points.txt +0 -3
- {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/LICENSE +0 -0
- {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/WHEEL +0 -0
edsl/questions/QuestionBase.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
"""This module contains the Question class, which is the base class for all questions in EDSL."""
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
|
+
import time
|
4
5
|
from abc import ABC, abstractmethod
|
5
|
-
from rich.table import Table
|
6
6
|
from typing import Any, Type, Optional, List, Callable
|
7
7
|
import copy
|
8
8
|
|
@@ -12,7 +12,7 @@ from edsl.exceptions import (
|
|
12
12
|
)
|
13
13
|
from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
|
14
14
|
|
15
|
-
|
15
|
+
|
16
16
|
from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
|
17
17
|
from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
|
18
18
|
from edsl.Base import PersistenceMixin, RichPrintingMixin
|
@@ -124,6 +124,8 @@ class QuestionBase(
|
|
124
124
|
:param model: The language model to use. If None, assumes does not matter.
|
125
125
|
|
126
126
|
"""
|
127
|
+
from edsl.prompts.registry import get_classes as prompt_lookup
|
128
|
+
|
127
129
|
applicable_prompts = prompt_lookup(
|
128
130
|
component_type="question_instructions",
|
129
131
|
question_type=cls.question_type,
|
@@ -173,15 +175,16 @@ class QuestionBase(
|
|
173
175
|
def add_model_instructions(
|
174
176
|
self, *, instructions: str, model: Optional[str] = None
|
175
177
|
) -> None:
|
176
|
-
"""Add model-specific instructions for the question.
|
178
|
+
"""Add model-specific instructions for the question that override the default instructions.
|
177
179
|
|
178
180
|
:param instructions: The instructions to add. This is typically a jinja2 template.
|
179
181
|
:param model: The language model for this instruction.
|
180
182
|
|
181
183
|
>>> from edsl.questions import QuestionFreeText
|
182
184
|
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
|
183
|
-
>>> q.add_model_instructions(instructions = "Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
|
184
|
-
|
185
|
+
>>> q.add_model_instructions(instructions = "{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
|
186
|
+
>>> q.get_instructions(model = "gpt3")
|
187
|
+
Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
|
185
188
|
"""
|
186
189
|
from edsl import Model
|
187
190
|
|
@@ -201,6 +204,13 @@ class QuestionBase(
|
|
201
204
|
"""Get the mathcing question-answering instructions for the question.
|
202
205
|
|
203
206
|
:param model: The language model to use.
|
207
|
+
|
208
|
+
>>> from edsl import QuestionFreeText
|
209
|
+
>>> QuestionFreeText.example().get_instructions()
|
210
|
+
Prompt(text=\"""You are being asked the following question: {{question_text}}
|
211
|
+
Return a valid JSON formatted like this:
|
212
|
+
{"answer": "<put free text answer here>"}
|
213
|
+
\""")
|
204
214
|
"""
|
205
215
|
from edsl.prompts.Prompt import Prompt
|
206
216
|
|
@@ -293,7 +303,16 @@ class QuestionBase(
|
|
293
303
|
print_json(json.dumps(self.to_dict()))
|
294
304
|
|
295
305
|
def __call__(self, just_answer=True, model=None, agent=None, **kwargs):
|
296
|
-
"""Call the question.
|
306
|
+
"""Call the question.
|
307
|
+
|
308
|
+
>>> from edsl.language_models import LanguageModel
|
309
|
+
>>> m = LanguageModel.example(canned_response = "Yo, what's up?", test_model = True)
|
310
|
+
>>> from edsl import QuestionFreeText
|
311
|
+
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
|
312
|
+
>>> q(model = m)
|
313
|
+
"Yo, what's up?"
|
314
|
+
|
315
|
+
"""
|
297
316
|
survey = self.to_survey()
|
298
317
|
results = survey(model=model, agent=agent, **kwargs)
|
299
318
|
if just_answer:
|
@@ -304,7 +323,6 @@ class QuestionBase(
|
|
304
323
|
async def run_async(self, just_answer=True, model=None, agent=None, **kwargs):
|
305
324
|
"""Call the question."""
|
306
325
|
survey = self.to_survey()
|
307
|
-
## asyncio.run(survey.async_call());
|
308
326
|
results = await survey.run_async(model=model, agent=agent, **kwargs)
|
309
327
|
if just_answer:
|
310
328
|
return results.select(f"answer.{self.question_name}").first()
|
@@ -383,29 +401,34 @@ class QuestionBase(
|
|
383
401
|
s = Survey([self, other])
|
384
402
|
return s
|
385
403
|
|
386
|
-
def to_survey(self):
|
404
|
+
def to_survey(self) -> "Survey":
|
387
405
|
"""Turn a single question into a survey."""
|
388
406
|
from edsl.surveys.Survey import Survey
|
389
407
|
|
390
408
|
s = Survey([self])
|
391
409
|
return s
|
392
410
|
|
393
|
-
def run(self, *args, **kwargs):
|
411
|
+
def run(self, *args, **kwargs) -> "Results":
|
394
412
|
"""Turn a single question into a survey and run it."""
|
395
413
|
from edsl.surveys.Survey import Survey
|
396
414
|
|
397
415
|
s = self.to_survey()
|
398
416
|
return s.run(*args, **kwargs)
|
399
417
|
|
400
|
-
def by(self, *args):
|
401
|
-
"""Turn a single question into a survey and
|
418
|
+
def by(self, *args) -> "Jobs":
|
419
|
+
"""Turn a single question into a survey and then a Job."""
|
402
420
|
from edsl.surveys.Survey import Survey
|
403
421
|
|
404
422
|
s = Survey([self])
|
405
423
|
return s.by(*args)
|
406
424
|
|
407
|
-
def human_readable(self):
|
408
|
-
"""Print the question in a human readable format.
|
425
|
+
def human_readable(self) -> str:
|
426
|
+
"""Print the question in a human readable format.
|
427
|
+
|
428
|
+
>>> from edsl.questions import QuestionFreeText
|
429
|
+
>>> QuestionFreeText.example().human_readable()
|
430
|
+
'Question Type: free_text\\nQuestion: How are you?'
|
431
|
+
"""
|
409
432
|
lines = []
|
410
433
|
lines.append(f"Question Type: {self.question_type}")
|
411
434
|
lines.append(f"Question: {self.question_text}")
|
@@ -475,6 +498,8 @@ class QuestionBase(
|
|
475
498
|
|
476
499
|
def rich_print(self):
|
477
500
|
"""Print the question in a rich format."""
|
501
|
+
from rich.table import Table
|
502
|
+
|
478
503
|
table = Table(show_header=True, header_style="bold magenta")
|
479
504
|
table.add_column("Question Name", style="dim")
|
480
505
|
table.add_column("Question Type")
|
edsl/questions/QuestionBudget.py
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import random
|
3
|
-
import textwrap
|
4
3
|
from typing import Any, Optional, Union
|
5
4
|
from edsl.questions.QuestionBase import QuestionBase
|
6
5
|
from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
|
7
|
-
from edsl.scenarios import Scenario
|
8
|
-
from edsl.utilities import random_string
|
9
6
|
|
10
7
|
|
11
8
|
class QuestionBudget(QuestionBase):
|
@@ -46,7 +43,7 @@ class QuestionBudget(QuestionBase):
|
|
46
43
|
return answer
|
47
44
|
|
48
45
|
def _translate_answer_code_to_answer(
|
49
|
-
self, answer_codes: dict[str, int], scenario: Scenario = None
|
46
|
+
self, answer_codes: dict[str, int], scenario: "Scenario" = None
|
50
47
|
):
|
51
48
|
"""
|
52
49
|
Translate the answer codes to the actual answers.
|
@@ -63,6 +60,8 @@ class QuestionBudget(QuestionBase):
|
|
63
60
|
|
64
61
|
def _simulate_answer(self, human_readable=True):
|
65
62
|
"""Simulate a valid answer for debugging purposes (what the validator expects)."""
|
63
|
+
from edsl.utilities.utilities import random_string
|
64
|
+
|
66
65
|
if human_readable:
|
67
66
|
keys = self.question_options
|
68
67
|
else:
|
@@ -163,8 +162,8 @@ def main():
|
|
163
162
|
|
164
163
|
|
165
164
|
if __name__ == "__main__":
|
166
|
-
q = QuestionBudget.example()
|
167
|
-
results = q.run()
|
165
|
+
# q = QuestionBudget.example()
|
166
|
+
# results = q.run()
|
168
167
|
|
169
168
|
import doctest
|
170
169
|
|
@@ -9,8 +9,6 @@ from edsl.questions.descriptors import (
|
|
9
9
|
IntegerDescriptor,
|
10
10
|
QuestionOptionsDescriptor,
|
11
11
|
)
|
12
|
-
from edsl.scenarios import Scenario
|
13
|
-
from edsl.utilities import random_string
|
14
12
|
|
15
13
|
|
16
14
|
class QuestionCheckBox(QuestionBase):
|
@@ -55,13 +53,17 @@ class QuestionCheckBox(QuestionBase):
|
|
55
53
|
self._validate_answer_checkbox(answer)
|
56
54
|
return answer
|
57
55
|
|
58
|
-
def _translate_answer_code_to_answer(
|
56
|
+
def _translate_answer_code_to_answer(
|
57
|
+
self, answer_codes, scenario: "Scenario" = None
|
58
|
+
):
|
59
59
|
"""
|
60
60
|
Translate the answer code to the actual answer.
|
61
61
|
|
62
62
|
For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
|
63
63
|
The LLM will respond with [0,1] and this code will translate it to ["a","b"].
|
64
64
|
"""
|
65
|
+
from edsl.scenarios.Scenario import Scenario
|
66
|
+
|
65
67
|
scenario = scenario or Scenario()
|
66
68
|
translated_options = [
|
67
69
|
Template(option).render(scenario) for option in self.question_options
|
@@ -73,6 +75,8 @@ class QuestionCheckBox(QuestionBase):
|
|
73
75
|
|
74
76
|
def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
|
75
77
|
"""Simulate a valid answer for debugging purposes."""
|
78
|
+
from edsl.utilities.utilities import random_string
|
79
|
+
|
76
80
|
min_selections = self.min_selections or 1
|
77
81
|
max_selections = self.max_selections or len(self.question_options)
|
78
82
|
num_selections = random.randint(min_selections, max_selections)
|
@@ -2,8 +2,6 @@ from __future__ import annotations
|
|
2
2
|
from typing import Any
|
3
3
|
from edsl.questions.QuestionBase import QuestionBase
|
4
4
|
from edsl.questions.descriptors import AnswerTemplateDescriptor
|
5
|
-
from edsl.scenarios import Scenario
|
6
|
-
from edsl.utilities import random_string
|
7
5
|
|
8
6
|
|
9
7
|
class QuestionExtract(QuestionBase):
|
@@ -44,12 +42,14 @@ class QuestionExtract(QuestionBase):
|
|
44
42
|
self._validate_answer_extract(answer)
|
45
43
|
return answer
|
46
44
|
|
47
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
45
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
48
46
|
"""Return the answer in a human-readable format."""
|
49
47
|
return answer
|
50
48
|
|
51
49
|
def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
|
52
50
|
"""Simulate a valid answer for debugging purposes."""
|
51
|
+
from edsl.utilities.utilities import random_string
|
52
|
+
|
53
53
|
return {
|
54
54
|
"answer": {key: random_string() for key in self.answer_template.keys()},
|
55
55
|
"comment": random_string(),
|
@@ -106,6 +106,8 @@ def main():
|
|
106
106
|
q.to_dict()
|
107
107
|
assert q.from_dict(q.to_dict()) == q
|
108
108
|
|
109
|
+
|
110
|
+
if __name__ == "__main__":
|
109
111
|
import doctest
|
110
112
|
|
111
113
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -2,8 +2,6 @@ from __future__ import annotations
|
|
2
2
|
import textwrap
|
3
3
|
from typing import Any, Optional
|
4
4
|
from edsl.questions.QuestionBase import QuestionBase
|
5
|
-
from edsl.scenarios import Scenario
|
6
|
-
from edsl.utilities import random_string
|
7
5
|
|
8
6
|
|
9
7
|
class QuestionFreeText(QuestionBase):
|
@@ -43,12 +41,14 @@ class QuestionFreeText(QuestionBase):
|
|
43
41
|
self._validate_answer_key_value(answer, "answer", str)
|
44
42
|
return answer
|
45
43
|
|
46
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
44
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
47
45
|
"""Do nothing, because the answer is already in a human-readable format."""
|
48
46
|
return answer
|
49
47
|
|
50
48
|
def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
|
51
49
|
"""Simulate a valid answer for debugging purposes."""
|
50
|
+
from edsl.utilities.utilities import random_string
|
51
|
+
|
52
52
|
return {"answer": random_string()}
|
53
53
|
|
54
54
|
@property
|
@@ -1,10 +1,7 @@
|
|
1
1
|
from typing import Optional, Callable
|
2
|
-
from edsl.questions.QuestionBase import QuestionBase
|
3
|
-
from edsl.questions.descriptors import FunctionDescriptor
|
4
2
|
import inspect
|
5
3
|
|
6
4
|
from edsl.questions.QuestionBase import QuestionBase
|
7
|
-
from edsl.questions.descriptors import FunctionDescriptor
|
8
5
|
|
9
6
|
from edsl.utilities.restricted_python import create_restricted_function
|
10
7
|
|
edsl/questions/QuestionList.py
CHANGED
@@ -5,9 +5,6 @@ from typing import Any, Optional, Union
|
|
5
5
|
from edsl.questions.QuestionBase import QuestionBase
|
6
6
|
from edsl.questions.descriptors import IntegerOrNoneDescriptor
|
7
7
|
|
8
|
-
from edsl.scenarios import Scenario
|
9
|
-
from edsl.utilities import random_string
|
10
|
-
|
11
8
|
|
12
9
|
class QuestionList(QuestionBase):
|
13
10
|
"""This question prompts the agent to answer by providing a list of items as comma-separated strings."""
|
@@ -42,13 +39,15 @@ class QuestionList(QuestionBase):
|
|
42
39
|
self._validate_answer_list(answer)
|
43
40
|
return answer
|
44
41
|
|
45
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
42
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
46
43
|
"""There is no answer code."""
|
47
44
|
return answer
|
48
45
|
|
49
46
|
def _simulate_answer(self, human_readable: bool = True):
|
50
47
|
"""Simulate a valid answer for debugging purposes (what the validator expects)."""
|
51
48
|
num_items = random.randint(1, self.max_list_items or 2)
|
49
|
+
from edsl.utilities.utilities import random_string
|
50
|
+
|
52
51
|
return {"answer": [random_string() for _ in range(num_items)]}
|
53
52
|
|
54
53
|
@property
|
@@ -1,13 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
|
2
|
+
import time
|
3
|
+
from typing import Union
|
3
4
|
import random
|
4
5
|
|
5
6
|
from jinja2 import Template
|
6
7
|
|
7
|
-
from edsl.utilities import random_string
|
8
|
-
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
9
8
|
from edsl.questions.QuestionBase import QuestionBase
|
10
|
-
from edsl.
|
9
|
+
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
11
10
|
|
12
11
|
|
13
12
|
class QuestionMultipleChoice(QuestionBase):
|
@@ -15,9 +14,9 @@ class QuestionMultipleChoice(QuestionBase):
|
|
15
14
|
|
16
15
|
question_type = "multiple_choice"
|
17
16
|
purpose = "When options are known and limited"
|
18
|
-
question_options: Union[
|
19
|
-
|
20
|
-
|
17
|
+
question_options: Union[list[str], list[list], list[float], list[int]] = (
|
18
|
+
QuestionOptionsDescriptor()
|
19
|
+
)
|
21
20
|
|
22
21
|
def __init__(
|
23
22
|
self,
|
@@ -47,8 +46,12 @@ class QuestionMultipleChoice(QuestionBase):
|
|
47
46
|
self._validate_answer_multiple_choice(answer)
|
48
47
|
return answer
|
49
48
|
|
50
|
-
def _translate_answer_code_to_answer(
|
49
|
+
def _translate_answer_code_to_answer(
|
50
|
+
self, answer_code, scenario: "Scenario" = None
|
51
|
+
):
|
51
52
|
"""Translate the answer code to the actual answer."""
|
53
|
+
from edsl.scenarios.Scenario import Scenario
|
54
|
+
|
52
55
|
scenario = scenario or Scenario()
|
53
56
|
translated_options = [
|
54
57
|
Template(str(option)).render(scenario) for option in self.question_options
|
@@ -59,6 +62,8 @@ class QuestionMultipleChoice(QuestionBase):
|
|
59
62
|
self, human_readable: bool = True
|
60
63
|
) -> dict[str, Union[int, str]]:
|
61
64
|
"""Simulate a valid answer for debugging purposes."""
|
65
|
+
from edsl.utilities.utilities import random_string
|
66
|
+
|
62
67
|
if human_readable:
|
63
68
|
answer = random.choice(self.question_options)
|
64
69
|
else:
|
@@ -70,6 +75,7 @@ class QuestionMultipleChoice(QuestionBase):
|
|
70
75
|
|
71
76
|
@property
|
72
77
|
def question_html_content(self) -> str:
|
78
|
+
|
73
79
|
if hasattr(self, "option_labels"):
|
74
80
|
option_labels = self.option_labels
|
75
81
|
else:
|
@@ -127,6 +133,8 @@ def main():
|
|
127
133
|
q.to_dict()
|
128
134
|
assert q.from_dict(q.to_dict()) == q
|
129
135
|
|
136
|
+
|
137
|
+
if __name__ == "__main__":
|
130
138
|
import doctest
|
131
139
|
|
132
140
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -2,11 +2,10 @@ from __future__ import annotations
|
|
2
2
|
import textwrap
|
3
3
|
from random import uniform
|
4
4
|
from typing import Any, Optional, Union
|
5
|
+
|
5
6
|
from edsl.exceptions import QuestionAnswerValidationError
|
6
7
|
from edsl.questions.QuestionBase import QuestionBase
|
7
8
|
from edsl.questions.descriptors import NumericalOrNoneDescriptor
|
8
|
-
from edsl.scenarios import Scenario
|
9
|
-
from edsl.utilities import random_string
|
10
9
|
|
11
10
|
|
12
11
|
class QuestionNumerical(QuestionBase):
|
@@ -48,12 +47,14 @@ class QuestionNumerical(QuestionBase):
|
|
48
47
|
self._validate_answer_numerical(answer)
|
49
48
|
return answer
|
50
49
|
|
51
|
-
def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
|
50
|
+
def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
|
52
51
|
"""There is no answer code."""
|
53
52
|
return answer
|
54
53
|
|
55
54
|
def _simulate_answer(self, human_readable: bool = True):
|
56
55
|
"""Simulate a valid answer for debugging purposes."""
|
56
|
+
from edsl.utilities.utilities import random_string
|
57
|
+
|
57
58
|
return {
|
58
59
|
"answer": uniform(self.min_value, self.max_value),
|
59
60
|
"comment": random_string(),
|
edsl/questions/QuestionRank.py
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import random
|
3
|
-
import textwrap
|
4
3
|
from jinja2 import Template
|
5
4
|
from typing import Any, Optional, Union
|
6
5
|
from edsl.questions.QuestionBase import QuestionBase
|
7
6
|
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
-
|
9
|
-
from edsl.utilities.utilities import random_string
|
7
|
+
|
10
8
|
from edsl.questions.descriptors import (
|
11
9
|
QuestionOptionsDescriptor,
|
12
10
|
NumSelectionsDescriptor,
|
@@ -55,6 +53,8 @@ class QuestionRank(QuestionBase):
|
|
55
53
|
self, answer_codes, scenario: Scenario = None
|
56
54
|
) -> list[str]:
|
57
55
|
"""Translate the answer code to the actual answer."""
|
56
|
+
from edsl.scenarios import Scenario
|
57
|
+
|
58
58
|
scenario = scenario or Scenario()
|
59
59
|
translated_options = [
|
60
60
|
Template(option).render(scenario) for option in self.question_options
|
@@ -66,6 +66,8 @@ class QuestionRank(QuestionBase):
|
|
66
66
|
|
67
67
|
def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
|
68
68
|
"""Simulate a valid answer for debugging purposes."""
|
69
|
+
from edsl.utilities.utilities import random_string
|
70
|
+
|
69
71
|
if human_readable:
|
70
72
|
selected = random.sample(self.question_options, self.num_selections)
|
71
73
|
else:
|
edsl/questions/__init__.py
CHANGED
@@ -10,17 +10,18 @@ from edsl.questions.QuestionBudget import QuestionBudget
|
|
10
10
|
from edsl.questions.QuestionCheckBox import QuestionCheckBox
|
11
11
|
from edsl.questions.QuestionExtract import QuestionExtract
|
12
12
|
from edsl.questions.QuestionFreeText import QuestionFreeText
|
13
|
+
|
13
14
|
from edsl.questions.QuestionFunctional import QuestionFunctional
|
14
15
|
from edsl.questions.QuestionList import QuestionList
|
15
16
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
16
17
|
from edsl.questions.QuestionNumerical import QuestionNumerical
|
17
18
|
from edsl.questions.QuestionRank import QuestionRank
|
18
19
|
|
19
|
-
# Questions derived from core questions
|
20
|
+
# # Questions derived from core questions
|
20
21
|
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
21
22
|
from edsl.questions.derived.QuestionLinearScale import QuestionLinearScale
|
22
23
|
from edsl.questions.derived.QuestionTopK import QuestionTopK
|
23
24
|
from edsl.questions.derived.QuestionYesNo import QuestionYesNo
|
24
25
|
|
25
|
-
# Compose Questions
|
26
|
-
from edsl.questions.compose_questions import compose_questions
|
26
|
+
# # Compose Questions
|
27
|
+
# from edsl.questions.compose_questions import compose_questions
|
edsl/questions/descriptors.py
CHANGED
@@ -8,9 +8,7 @@ from edsl.exceptions import (
|
|
8
8
|
QuestionAnswerValidationError,
|
9
9
|
)
|
10
10
|
from edsl.questions.settings import Settings
|
11
|
-
from edsl.utilities.utilities import is_valid_variable_name
|
12
11
|
|
13
|
-
from edsl.prompts import get_classes
|
14
12
|
|
15
13
|
################################
|
16
14
|
# Helper functions
|
@@ -56,6 +54,8 @@ class BaseDescriptor(ABC):
|
|
56
54
|
def __set__(self, instance, value: Any) -> None:
|
57
55
|
"""Set the value of the attribute."""
|
58
56
|
self.validate(value, instance)
|
57
|
+
from edsl.prompts.registry import get_classes
|
58
|
+
|
59
59
|
instance.__dict__[self.name] = value
|
60
60
|
if self.name == "_instructions":
|
61
61
|
instructions = value
|
@@ -231,6 +231,8 @@ class QuestionNameDescriptor(BaseDescriptor):
|
|
231
231
|
|
232
232
|
def validate(self, value, instance):
|
233
233
|
"""Validate the value is a valid variable name."""
|
234
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
235
|
+
|
234
236
|
if not is_valid_variable_name(value):
|
235
237
|
raise QuestionCreationValidationError(
|
236
238
|
f"`question_name` is not a valid variable name (got {value})."
|
@@ -1,10 +1,10 @@
|
|
1
1
|
"""This module provides a factory class for creating question objects."""
|
2
2
|
|
3
3
|
import textwrap
|
4
|
-
from
|
4
|
+
from uuid import UUID
|
5
|
+
from typing import Any, Optional, Union
|
6
|
+
|
5
7
|
|
6
|
-
from edsl.exceptions import QuestionSerializationError
|
7
|
-
from edsl.exceptions import QuestionCreationValidationError
|
8
8
|
from edsl.questions.QuestionBase import RegisterQuestionsMeta
|
9
9
|
|
10
10
|
|
@@ -60,46 +60,35 @@ class Question(metaclass=Meta):
|
|
60
60
|
return q.example()
|
61
61
|
|
62
62
|
@classmethod
|
63
|
-
def pull(cls,
|
63
|
+
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
64
64
|
"""Pull the object from coop."""
|
65
65
|
from edsl.coop import Coop
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
id = id_or_url.split("/")[-1]
|
70
|
-
else:
|
71
|
-
id = id_or_url
|
72
|
-
from edsl.questions.QuestionBase import QuestionBase
|
73
|
-
|
74
|
-
return c._get_base(QuestionBase, id)
|
67
|
+
coop = Coop()
|
68
|
+
return coop.get(uuid, url, "question")
|
75
69
|
|
76
70
|
@classmethod
|
77
|
-
def delete(cls,
|
71
|
+
def delete(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
78
72
|
"""Delete the object from coop."""
|
79
73
|
from edsl.coop import Coop
|
80
74
|
|
81
|
-
|
82
|
-
|
83
|
-
id = id_or_url.split("/")[-1]
|
84
|
-
else:
|
85
|
-
id = id_or_url
|
86
|
-
from edsl.questions.QuestionBase import QuestionBase
|
87
|
-
|
88
|
-
return c._delete_base(QuestionBase, id)
|
75
|
+
coop = Coop()
|
76
|
+
return coop.delete(uuid, url)
|
89
77
|
|
90
78
|
@classmethod
|
91
|
-
def
|
92
|
-
|
79
|
+
def patch(
|
80
|
+
cls,
|
81
|
+
uuid: Optional[Union[str, UUID]] = None,
|
82
|
+
url: Optional[str] = None,
|
83
|
+
description: Optional[str] = None,
|
84
|
+
value: Optional[Any] = None,
|
85
|
+
visibility: Optional[str] = None,
|
86
|
+
):
|
87
|
+
"""Patch the object on coop."""
|
93
88
|
from edsl.coop import Coop
|
94
89
|
|
95
|
-
|
96
|
-
|
97
|
-
id = id_or_url.split("/")[-1]
|
98
|
-
else:
|
99
|
-
id = id_or_url
|
100
|
-
from edsl.questions.QuestionBase import QuestionBase
|
101
|
-
|
102
|
-
return c._update_base(QuestionBase, id, visibility)
|
90
|
+
coop = Coop()
|
91
|
+
return coop.patch(uuid, url, description, value, visibility)
|
103
92
|
|
104
93
|
@classmethod
|
105
94
|
def available(cls, show_class_names: bool = False) -> Union[list, dict]:
|
edsl/questions/settings.py
CHANGED
edsl/results/Dataset.py
CHANGED
@@ -78,6 +78,28 @@ class Dataset(UserList, ResultsExportMixin):
|
|
78
78
|
|
79
79
|
return get_values(self.data[0])[0]
|
80
80
|
|
81
|
+
def select(self, *keys):
|
82
|
+
"""Return a new dataset with only the selected keys.
|
83
|
+
|
84
|
+
:param keys: The keys to select.
|
85
|
+
|
86
|
+
>>> d = Dataset([{'a.b':[1,2,3,4]}, {'c.d':[5,6,7,8]}])
|
87
|
+
>>> d.select('a.b')
|
88
|
+
Dataset([{'a.b': [1, 2, 3, 4]}])
|
89
|
+
|
90
|
+
>>> d.select('a.b', 'c.d')
|
91
|
+
Dataset([{'a.b': [1, 2, 3, 4]}, {'c.d': [5, 6, 7, 8]}])
|
92
|
+
"""
|
93
|
+
if isinstance(keys, str):
|
94
|
+
keys = [keys]
|
95
|
+
|
96
|
+
new_data = []
|
97
|
+
for observation in self.data:
|
98
|
+
observation_key = list(observation.keys())[0]
|
99
|
+
if observation_key in keys:
|
100
|
+
new_data.append(observation)
|
101
|
+
return Dataset(new_data)
|
102
|
+
|
81
103
|
def _repr_html_(self) -> str:
|
82
104
|
"""Return an HTML representation of the dataset."""
|
83
105
|
from edsl.utilities.utilities import data_to_html
|
@@ -223,6 +245,15 @@ class Dataset(UserList, ResultsExportMixin):
|
|
223
245
|
|
224
246
|
return Dataset(new_data)
|
225
247
|
|
248
|
+
@classmethod
|
249
|
+
def example(self):
|
250
|
+
"""Return an example dataset.
|
251
|
+
|
252
|
+
>>> Dataset.example()
|
253
|
+
Dataset([{'a': [1, 2, 3, 4]}, {'b': [4, 3, 2, 1]}])
|
254
|
+
"""
|
255
|
+
return Dataset([{"a": [1, 2, 3, 4]}, {"b": [4, 3, 2, 1]}])
|
256
|
+
|
226
257
|
|
227
258
|
if __name__ == "__main__":
|
228
259
|
import doctest
|