edsl 0.1.36.dev6__py3-none-any.whl → 0.1.37.dev2__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 +303 -303
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +48 -47
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +804 -804
- edsl/agents/AgentList.py +345 -337
- edsl/agents/Invigilator.py +222 -222
- edsl/agents/InvigilatorBase.py +305 -294
- edsl/agents/PromptConstructor.py +312 -312
- edsl/agents/__init__.py +3 -3
- edsl/agents/descriptors.py +86 -86
- edsl/agents/prompt_helpers.py +129 -129
- edsl/auto/AutoStudy.py +117 -117
- edsl/auto/StageBase.py +230 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +73 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +224 -224
- edsl/base/Base.py +289 -289
- edsl/config.py +149 -149
- edsl/conjure/AgentConstructionMixin.py +152 -152
- edsl/conjure/Conjure.py +62 -62
- edsl/conjure/InputData.py +659 -659
- edsl/conjure/InputDataCSV.py +48 -48
- edsl/conjure/InputDataMixinQuestionStats.py +182 -182
- edsl/conjure/InputDataPyRead.py +91 -91
- edsl/conjure/InputDataSPSS.py +8 -8
- edsl/conjure/InputDataStata.py +8 -8
- edsl/conjure/QuestionOptionMixin.py +76 -76
- edsl/conjure/QuestionTypeMixin.py +23 -23
- edsl/conjure/RawQuestion.py +65 -65
- edsl/conjure/SurveyResponses.py +7 -7
- edsl/conjure/__init__.py +9 -9
- edsl/conjure/naming_utilities.py +263 -263
- edsl/conjure/utilities.py +201 -201
- edsl/conversation/Conversation.py +238 -238
- edsl/conversation/car_buying.py +58 -58
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +824 -849
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +527 -527
- edsl/data/CacheEntry.py +228 -228
- edsl/data/CacheHandler.py +149 -149
- edsl/data/RemoteCacheSync.py +97 -84
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +4 -4
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +73 -73
- edsl/enums.py +173 -173
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +40 -40
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +91 -91
- edsl/exceptions/results.py +26 -26
- edsl/exceptions/surveys.py +34 -34
- edsl/inference_services/AnthropicService.py +87 -87
- edsl/inference_services/AwsBedrock.py +115 -115
- edsl/inference_services/AzureAI.py +217 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +156 -156
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +147 -147
- edsl/inference_services/InferenceServicesCollection.py +74 -72
- edsl/inference_services/MistralAIService.py +123 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +224 -224
- edsl/inference_services/TestService.py +89 -89
- edsl/inference_services/TogetherAIService.py +170 -170
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +39 -39
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/Answers.py +56 -56
- edsl/jobs/Jobs.py +1112 -1112
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/buckets/BucketCollection.py +63 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +248 -248
- edsl/jobs/interviews/Interview.py +661 -651
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +182 -182
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +338 -337
- edsl/jobs/runners/JobsRunnerStatus.py +332 -332
- edsl/jobs/tasks/QuestionTaskCreator.py +242 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +441 -441
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +163 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/LanguageModel.py +718 -718
- edsl/language_models/ModelList.py +102 -102
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/__init__.py +2 -2
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/registry.py +137 -137
- edsl/language_models/repair.py +156 -156
- edsl/language_models/unused/ReplicateBase.py +83 -83
- edsl/language_models/utilities.py +64 -64
- edsl/notebooks/Notebook.py +259 -259
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +353 -358
- edsl/prompts/__init__.py +2 -2
- edsl/questions/AnswerValidatorMixin.py +289 -289
- edsl/questions/QuestionBase.py +616 -616
- edsl/questions/QuestionBaseGenMixin.py +161 -161
- edsl/questions/QuestionBasePromptsMixin.py +266 -266
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +183 -183
- edsl/questions/QuestionFreeText.py +114 -113
- edsl/questions/QuestionFunctional.py +159 -159
- edsl/questions/QuestionList.py +231 -231
- edsl/questions/QuestionMultipleChoice.py +286 -286
- edsl/questions/QuestionNumerical.py +153 -153
- edsl/questions/QuestionRank.py +324 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/RegisterQuestionsMeta.py +71 -71
- edsl/questions/ResponseValidatorABC.py +174 -174
- edsl/questions/SimpleAskMixin.py +73 -73
- edsl/questions/__init__.py +26 -26
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +87 -87
- edsl/questions/derived/QuestionTopK.py +91 -91
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +418 -418
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/question_registry.py +147 -147
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/Dataset.py +293 -293
- edsl/results/DatasetExportMixin.py +693 -693
- edsl/results/DatasetTree.py +145 -145
- edsl/results/Result.py +435 -433
- edsl/results/Results.py +1160 -1158
- edsl/results/ResultsDBMixin.py +238 -238
- edsl/results/ResultsExportMixin.py +43 -43
- edsl/results/ResultsFetchMixin.py +33 -33
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/ResultsToolsMixin.py +98 -98
- edsl/results/Selector.py +118 -118
- edsl/results/__init__.py +2 -2
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/FileStore.py +458 -443
- edsl/scenarios/Scenario.py +510 -507
- edsl/scenarios/ScenarioHtmlMixin.py +59 -59
- edsl/scenarios/ScenarioList.py +1101 -1101
- edsl/scenarios/ScenarioListExportMixin.py +52 -52
- edsl/scenarios/ScenarioListPdfMixin.py +261 -261
- edsl/scenarios/__init__.py +4 -2
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +528 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +324 -324
- edsl/surveys/RuleCollection.py +387 -387
- edsl/surveys/Survey.py +1772 -1772
- edsl/surveys/SurveyCSS.py +261 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/SurveyFlowVisualizationMixin.py +121 -121
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/__init__.py +3 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +56 -56
- edsl/surveys/instructions/ChangeInstruction.py +47 -47
- edsl/surveys/instructions/Instruction.py +51 -51
- edsl/surveys/instructions/InstructionCollection.py +77 -77
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +9 -9
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +391 -391
- {edsl-0.1.36.dev6.dist-info → edsl-0.1.37.dev2.dist-info}/LICENSE +21 -21
- {edsl-0.1.36.dev6.dist-info → edsl-0.1.37.dev2.dist-info}/METADATA +1 -1
- edsl-0.1.37.dev2.dist-info/RECORD +279 -0
- edsl-0.1.36.dev6.dist-info/RECORD +0 -279
- {edsl-0.1.36.dev6.dist-info → edsl-0.1.37.dev2.dist-info}/WHEEL +0 -0
edsl/questions/Quick.py
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
from edsl import (
|
2
|
-
QuestionFreeText,
|
3
|
-
QuestionMultipleChoice,
|
4
|
-
Survey,
|
5
|
-
QuestionList,
|
6
|
-
Question,
|
7
|
-
)
|
8
|
-
|
9
|
-
|
10
|
-
def Quick(question_text):
|
11
|
-
q_type = QuestionMultipleChoice(
|
12
|
-
question_text=f"A researcher is asking a language model this: {question_text}. What is the most appropriate type of question to ask?",
|
13
|
-
question_name="potential_question_type",
|
14
|
-
question_options=["multiple_choice", "list", "free_text"],
|
15
|
-
)
|
16
|
-
|
17
|
-
q_name = QuestionFreeText(
|
18
|
-
question_text=f"A researcher is asking a language model this: {question_text}. What is a good name for this question that's a valid python identifier? Just return the proposed identifer",
|
19
|
-
question_name="potential_question_name",
|
20
|
-
)
|
21
|
-
|
22
|
-
q_options = QuestionList(
|
23
|
-
question_text=f"A research is asking this question: { question_text }. What are the possible options for this question?",
|
24
|
-
question_name="potential_question_options",
|
25
|
-
)
|
26
|
-
|
27
|
-
survey = Survey([q_type, q_name, q_options]).add_skip_rule(
|
28
|
-
q_options, "{{ potential_question_type }} != 'multiple_choice'"
|
29
|
-
)
|
30
|
-
return survey
|
31
|
-
# results = survey.run()
|
32
|
-
# question_type = results.select("potential_question_type").first()
|
33
|
-
# question_options = results.select("potential_question_options").first()
|
34
|
-
# question_name = results.select("potential_question_name").first()
|
35
|
-
# print("Question Type: ", question_type)
|
36
|
-
# print("Question Name: ", question_name)
|
37
|
-
# print("Question Options: ", question_options)
|
38
|
-
# if question_options == None:
|
39
|
-
# return Question(question_type, question_name = question_name)
|
40
|
-
# else:
|
41
|
-
# return Question(question_type, question_name = question_name, question_options = question_options)
|
1
|
+
from edsl import (
|
2
|
+
QuestionFreeText,
|
3
|
+
QuestionMultipleChoice,
|
4
|
+
Survey,
|
5
|
+
QuestionList,
|
6
|
+
Question,
|
7
|
+
)
|
8
|
+
|
9
|
+
|
10
|
+
def Quick(question_text):
|
11
|
+
q_type = QuestionMultipleChoice(
|
12
|
+
question_text=f"A researcher is asking a language model this: {question_text}. What is the most appropriate type of question to ask?",
|
13
|
+
question_name="potential_question_type",
|
14
|
+
question_options=["multiple_choice", "list", "free_text"],
|
15
|
+
)
|
16
|
+
|
17
|
+
q_name = QuestionFreeText(
|
18
|
+
question_text=f"A researcher is asking a language model this: {question_text}. What is a good name for this question that's a valid python identifier? Just return the proposed identifer",
|
19
|
+
question_name="potential_question_name",
|
20
|
+
)
|
21
|
+
|
22
|
+
q_options = QuestionList(
|
23
|
+
question_text=f"A research is asking this question: { question_text }. What are the possible options for this question?",
|
24
|
+
question_name="potential_question_options",
|
25
|
+
)
|
26
|
+
|
27
|
+
survey = Survey([q_type, q_name, q_options]).add_skip_rule(
|
28
|
+
q_options, "{{ potential_question_type }} != 'multiple_choice'"
|
29
|
+
)
|
30
|
+
return survey
|
31
|
+
# results = survey.run()
|
32
|
+
# question_type = results.select("potential_question_type").first()
|
33
|
+
# question_options = results.select("potential_question_options").first()
|
34
|
+
# question_name = results.select("potential_question_name").first()
|
35
|
+
# print("Question Type: ", question_type)
|
36
|
+
# print("Question Name: ", question_name)
|
37
|
+
# print("Question Options: ", question_options)
|
38
|
+
# if question_options == None:
|
39
|
+
# return Question(question_type, question_name = question_name)
|
40
|
+
# else:
|
41
|
+
# return Question(question_type, question_name = question_name, question_options = question_options)
|
@@ -1,71 +1,71 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from abc import ABCMeta
|
3
|
-
|
4
|
-
from edsl.enums import QuestionType
|
5
|
-
from edsl.exceptions.questions import QuestionMissingTypeError, QuestionBadTypeError
|
6
|
-
|
7
|
-
import inspect
|
8
|
-
|
9
|
-
|
10
|
-
class RegisterQuestionsMeta(ABCMeta):
|
11
|
-
"""Metaclass to register output elements in a registry i.e., those that have a parent."""
|
12
|
-
|
13
|
-
_registry = {} # Initialize the registry as a dictionary
|
14
|
-
|
15
|
-
def __init__(cls, name, bases, dct):
|
16
|
-
"""Initialize the class and adds it to the registry if it's not the base class."""
|
17
|
-
super(RegisterQuestionsMeta, cls).__init__(name, bases, dct)
|
18
|
-
if (
|
19
|
-
name != "QuestionBase"
|
20
|
-
and name != "QuestionFunctional"
|
21
|
-
and name != "QuestionAddTwoNumbers"
|
22
|
-
):
|
23
|
-
## Enforce that all questions have a question_type class attribute
|
24
|
-
## and it comes from our enum of valid question types.
|
25
|
-
required_attributes = [
|
26
|
-
"question_type",
|
27
|
-
"_response_model",
|
28
|
-
"response_validator_class",
|
29
|
-
]
|
30
|
-
for attr in required_attributes:
|
31
|
-
if not hasattr(cls, attr):
|
32
|
-
raise QuestionMissingTypeError(
|
33
|
-
f"Question must have a {attr} class attribute"
|
34
|
-
)
|
35
|
-
|
36
|
-
init_signature = inspect.signature(cls.__init__)
|
37
|
-
init_params = [p for p in init_signature.parameters if p != "self"]
|
38
|
-
required_params = [
|
39
|
-
"question_presentation",
|
40
|
-
"answering_instructions",
|
41
|
-
"question_name",
|
42
|
-
"question_text",
|
43
|
-
]
|
44
|
-
for param in required_params:
|
45
|
-
if param not in init_params:
|
46
|
-
raise QuestionBadTypeError(
|
47
|
-
f"Question type {name} must have a question_presentation parameter in its __init__ method"
|
48
|
-
)
|
49
|
-
|
50
|
-
if name != "QuestionBase":
|
51
|
-
RegisterQuestionsMeta._registry[name] = cls
|
52
|
-
|
53
|
-
@classmethod
|
54
|
-
def get_registered_classes(cls):
|
55
|
-
"""Return the registry of registered classes."""
|
56
|
-
return cls._registry
|
57
|
-
|
58
|
-
@classmethod
|
59
|
-
def question_types_to_classes(
|
60
|
-
cls,
|
61
|
-
):
|
62
|
-
"""Return a dictionary of question types to classes."""
|
63
|
-
d = {}
|
64
|
-
for classname, cls in cls._registry.items():
|
65
|
-
if hasattr(cls, "question_type"):
|
66
|
-
d[cls.question_type] = cls
|
67
|
-
else:
|
68
|
-
raise Exception(
|
69
|
-
f"Class {classname} does not have a question_type class attribute"
|
70
|
-
)
|
71
|
-
return d
|
1
|
+
from __future__ import annotations
|
2
|
+
from abc import ABCMeta
|
3
|
+
|
4
|
+
from edsl.enums import QuestionType
|
5
|
+
from edsl.exceptions.questions import QuestionMissingTypeError, QuestionBadTypeError
|
6
|
+
|
7
|
+
import inspect
|
8
|
+
|
9
|
+
|
10
|
+
class RegisterQuestionsMeta(ABCMeta):
|
11
|
+
"""Metaclass to register output elements in a registry i.e., those that have a parent."""
|
12
|
+
|
13
|
+
_registry = {} # Initialize the registry as a dictionary
|
14
|
+
|
15
|
+
def __init__(cls, name, bases, dct):
|
16
|
+
"""Initialize the class and adds it to the registry if it's not the base class."""
|
17
|
+
super(RegisterQuestionsMeta, cls).__init__(name, bases, dct)
|
18
|
+
if (
|
19
|
+
name != "QuestionBase"
|
20
|
+
and name != "QuestionFunctional"
|
21
|
+
and name != "QuestionAddTwoNumbers"
|
22
|
+
):
|
23
|
+
## Enforce that all questions have a question_type class attribute
|
24
|
+
## and it comes from our enum of valid question types.
|
25
|
+
required_attributes = [
|
26
|
+
"question_type",
|
27
|
+
"_response_model",
|
28
|
+
"response_validator_class",
|
29
|
+
]
|
30
|
+
for attr in required_attributes:
|
31
|
+
if not hasattr(cls, attr):
|
32
|
+
raise QuestionMissingTypeError(
|
33
|
+
f"Question must have a {attr} class attribute"
|
34
|
+
)
|
35
|
+
|
36
|
+
init_signature = inspect.signature(cls.__init__)
|
37
|
+
init_params = [p for p in init_signature.parameters if p != "self"]
|
38
|
+
required_params = [
|
39
|
+
"question_presentation",
|
40
|
+
"answering_instructions",
|
41
|
+
"question_name",
|
42
|
+
"question_text",
|
43
|
+
]
|
44
|
+
for param in required_params:
|
45
|
+
if param not in init_params:
|
46
|
+
raise QuestionBadTypeError(
|
47
|
+
f"Question type {name} must have a question_presentation parameter in its __init__ method"
|
48
|
+
)
|
49
|
+
|
50
|
+
if name != "QuestionBase":
|
51
|
+
RegisterQuestionsMeta._registry[name] = cls
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
def get_registered_classes(cls):
|
55
|
+
"""Return the registry of registered classes."""
|
56
|
+
return cls._registry
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def question_types_to_classes(
|
60
|
+
cls,
|
61
|
+
):
|
62
|
+
"""Return a dictionary of question types to classes."""
|
63
|
+
d = {}
|
64
|
+
for classname, cls in cls._registry.items():
|
65
|
+
if hasattr(cls, "question_type"):
|
66
|
+
d[cls.question_type] = cls
|
67
|
+
else:
|
68
|
+
raise Exception(
|
69
|
+
f"Class {classname} does not have a question_type class attribute"
|
70
|
+
)
|
71
|
+
return d
|
@@ -1,174 +1,174 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from pydantic import BaseModel, Field, field_validator
|
3
|
-
|
4
|
-
# from decimal import Decimal
|
5
|
-
from typing import Optional, Any, List, TypedDict
|
6
|
-
|
7
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
-
from pydantic import ValidationError
|
9
|
-
|
10
|
-
|
11
|
-
class BaseResponse(BaseModel):
|
12
|
-
answer: Any
|
13
|
-
comment: Optional[str] = None
|
14
|
-
generated_tokens: Optional[str] = None
|
15
|
-
|
16
|
-
|
17
|
-
class ResponseValidatorABC(ABC):
|
18
|
-
required_params: List[str] = []
|
19
|
-
|
20
|
-
def __init_subclass__(cls, **kwargs):
|
21
|
-
super().__init_subclass__(**kwargs)
|
22
|
-
required_class_vars = ["required_params", "valid_examples", "invalid_examples"]
|
23
|
-
for var in required_class_vars:
|
24
|
-
if not hasattr(cls, var):
|
25
|
-
raise ValueError(f"Class {cls.__name__} must have a '{var}' attribute.")
|
26
|
-
|
27
|
-
def __init__(
|
28
|
-
self,
|
29
|
-
response_model: type[BaseModel],
|
30
|
-
exception_to_throw: Optional[Exception] = None,
|
31
|
-
override_answer: Optional[dict] = None,
|
32
|
-
**kwargs,
|
33
|
-
):
|
34
|
-
self.response_model = response_model
|
35
|
-
self.exception_to_throw = exception_to_throw # for testing
|
36
|
-
self.override_answer = override_answer # for testing
|
37
|
-
self.original_exception = None
|
38
|
-
|
39
|
-
# Validate required parameters
|
40
|
-
missing_params = [
|
41
|
-
param for param in self.required_params if param not in kwargs
|
42
|
-
]
|
43
|
-
if missing_params:
|
44
|
-
raise ValueError(
|
45
|
-
f"Missing required parameters: {', '.join(missing_params)}"
|
46
|
-
)
|
47
|
-
|
48
|
-
# Set attributes
|
49
|
-
for key, value in kwargs.items():
|
50
|
-
setattr(self, key, value)
|
51
|
-
|
52
|
-
if not hasattr(self, "permissive"):
|
53
|
-
self.permissive = False
|
54
|
-
|
55
|
-
self.fixes_tried = 0
|
56
|
-
|
57
|
-
class RawEdslAnswerDict(TypedDict):
|
58
|
-
answer: Any
|
59
|
-
comment: Optional[str]
|
60
|
-
generated_tokens: Optional[str]
|
61
|
-
|
62
|
-
def _preprocess(self, data: RawEdslAnswerDict) -> RawEdslAnswerDict:
|
63
|
-
"""This is for testing purposes. A question can be given an exception to throw or an answer to always return.
|
64
|
-
|
65
|
-
>>> rv = ResponseValidatorABC.example()
|
66
|
-
>>> rv.override_answer = {"answer": 42}
|
67
|
-
>>> rv.validate({"answer": 23})
|
68
|
-
{'answer': 42, 'comment': None, 'generated_tokens': None}
|
69
|
-
"""
|
70
|
-
if self.exception_to_throw:
|
71
|
-
raise self.exception_to_throw
|
72
|
-
return self.override_answer if self.override_answer else data
|
73
|
-
|
74
|
-
def _base_validate(self, data: RawEdslAnswerDict) -> BaseModel:
|
75
|
-
"""This is the main validation function. It takes the response_model and checks the data against it, returning the instantiated model.
|
76
|
-
|
77
|
-
>>> rv = ResponseValidatorABC.example("numerical")
|
78
|
-
>>> rv._base_validate({"answer": 42})
|
79
|
-
ConstrainedNumericResponse(answer=42, comment=None, generated_tokens=None)
|
80
|
-
"""
|
81
|
-
try:
|
82
|
-
return self.response_model(**data)
|
83
|
-
except ValidationError as e:
|
84
|
-
raise QuestionAnswerValidationError(e, data=data, model=self.response_model)
|
85
|
-
|
86
|
-
def post_validation_answer_convert(self, data):
|
87
|
-
return data
|
88
|
-
|
89
|
-
class EdslAnswerDict(TypedDict):
|
90
|
-
answer: Any
|
91
|
-
comment: Optional[str]
|
92
|
-
generated_tokens: Optional[str]
|
93
|
-
|
94
|
-
def validate(
|
95
|
-
self,
|
96
|
-
raw_edsl_answer_dict: RawEdslAnswerDict,
|
97
|
-
fix=False,
|
98
|
-
verbose=False,
|
99
|
-
replacement_dict: dict = None,
|
100
|
-
) -> EdslAnswerDict:
|
101
|
-
"""This is the main validation function.
|
102
|
-
|
103
|
-
>>> rv = ResponseValidatorABC.example("numerical")
|
104
|
-
>>> rv.validate({"answer": 42})
|
105
|
-
{'answer': 42, 'comment': None, 'generated_tokens': None}
|
106
|
-
>>> rv.max_value
|
107
|
-
86.7
|
108
|
-
>>> rv.validate({"answer": "120"})
|
109
|
-
Traceback (most recent call last):
|
110
|
-
...
|
111
|
-
edsl.exceptions.questions.QuestionAnswerValidationError:...
|
112
|
-
>>> from edsl import QuestionNumerical
|
113
|
-
>>> q = QuestionNumerical.example()
|
114
|
-
>>> q.permissive = True
|
115
|
-
>>> rv = q.response_validator
|
116
|
-
>>> rv.validate({"answer": "120"})
|
117
|
-
{'answer': 120, 'comment': None, 'generated_tokens': None}
|
118
|
-
>>> rv.validate({"answer": "poo"})
|
119
|
-
Traceback (most recent call last):
|
120
|
-
...
|
121
|
-
edsl.exceptions.questions.QuestionAnswerValidationError:...
|
122
|
-
"""
|
123
|
-
proposed_edsl_answer_dict = self._preprocess(raw_edsl_answer_dict)
|
124
|
-
try:
|
125
|
-
pydantic_edsl_answer: BaseModel = self._base_validate(
|
126
|
-
proposed_edsl_answer_dict
|
127
|
-
)
|
128
|
-
edsl_answer_dict = self._extract_answer(pydantic_edsl_answer)
|
129
|
-
return self._post_process(edsl_answer_dict)
|
130
|
-
except QuestionAnswerValidationError as e:
|
131
|
-
if verbose:
|
132
|
-
print(f"Failed to validate {raw_edsl_answer_dict}; {str(e)}")
|
133
|
-
return self._handle_exception(e, raw_edsl_answer_dict)
|
134
|
-
|
135
|
-
def _handle_exception(self, e: Exception, raw_edsl_answer_dict) -> EdslAnswerDict:
|
136
|
-
if self.fixes_tried == 0:
|
137
|
-
self.original_exception = e
|
138
|
-
|
139
|
-
if self.fixes_tried == 0 and hasattr(self, "fix"):
|
140
|
-
self.fixes_tried += 1
|
141
|
-
fixed_data = self.fix(raw_edsl_answer_dict)
|
142
|
-
try:
|
143
|
-
return self.validate(fixed_data, fix=True)
|
144
|
-
except Exception as e:
|
145
|
-
pass # we don't log failed fixes
|
146
|
-
|
147
|
-
raise QuestionAnswerValidationError(
|
148
|
-
self.original_exception,
|
149
|
-
data=raw_edsl_answer_dict,
|
150
|
-
model=self.response_model,
|
151
|
-
)
|
152
|
-
|
153
|
-
def _check_constraints(self, pydantic_edsl_answer: BaseModel) -> dict:
|
154
|
-
pass
|
155
|
-
|
156
|
-
def _extract_answer(self, response: BaseModel) -> EdslAnswerDict:
|
157
|
-
return response.model_dump()
|
158
|
-
|
159
|
-
def _post_process(self, edsl_answer_dict: EdslAnswerDict) -> EdslAnswerDict:
|
160
|
-
return edsl_answer_dict
|
161
|
-
|
162
|
-
@classmethod
|
163
|
-
def example(cls, question_type="numerical"):
|
164
|
-
from edsl import Question
|
165
|
-
|
166
|
-
q = Question.example(question_type)
|
167
|
-
return q.response_validator
|
168
|
-
|
169
|
-
|
170
|
-
# Example usage
|
171
|
-
if __name__ == "__main__":
|
172
|
-
import doctest
|
173
|
-
|
174
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from pydantic import BaseModel, Field, field_validator
|
3
|
+
|
4
|
+
# from decimal import Decimal
|
5
|
+
from typing import Optional, Any, List, TypedDict
|
6
|
+
|
7
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
+
from pydantic import ValidationError
|
9
|
+
|
10
|
+
|
11
|
+
class BaseResponse(BaseModel):
|
12
|
+
answer: Any
|
13
|
+
comment: Optional[str] = None
|
14
|
+
generated_tokens: Optional[str] = None
|
15
|
+
|
16
|
+
|
17
|
+
class ResponseValidatorABC(ABC):
|
18
|
+
required_params: List[str] = []
|
19
|
+
|
20
|
+
def __init_subclass__(cls, **kwargs):
|
21
|
+
super().__init_subclass__(**kwargs)
|
22
|
+
required_class_vars = ["required_params", "valid_examples", "invalid_examples"]
|
23
|
+
for var in required_class_vars:
|
24
|
+
if not hasattr(cls, var):
|
25
|
+
raise ValueError(f"Class {cls.__name__} must have a '{var}' attribute.")
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
response_model: type[BaseModel],
|
30
|
+
exception_to_throw: Optional[Exception] = None,
|
31
|
+
override_answer: Optional[dict] = None,
|
32
|
+
**kwargs,
|
33
|
+
):
|
34
|
+
self.response_model = response_model
|
35
|
+
self.exception_to_throw = exception_to_throw # for testing
|
36
|
+
self.override_answer = override_answer # for testing
|
37
|
+
self.original_exception = None
|
38
|
+
|
39
|
+
# Validate required parameters
|
40
|
+
missing_params = [
|
41
|
+
param for param in self.required_params if param not in kwargs
|
42
|
+
]
|
43
|
+
if missing_params:
|
44
|
+
raise ValueError(
|
45
|
+
f"Missing required parameters: {', '.join(missing_params)}"
|
46
|
+
)
|
47
|
+
|
48
|
+
# Set attributes
|
49
|
+
for key, value in kwargs.items():
|
50
|
+
setattr(self, key, value)
|
51
|
+
|
52
|
+
if not hasattr(self, "permissive"):
|
53
|
+
self.permissive = False
|
54
|
+
|
55
|
+
self.fixes_tried = 0
|
56
|
+
|
57
|
+
class RawEdslAnswerDict(TypedDict):
|
58
|
+
answer: Any
|
59
|
+
comment: Optional[str]
|
60
|
+
generated_tokens: Optional[str]
|
61
|
+
|
62
|
+
def _preprocess(self, data: RawEdslAnswerDict) -> RawEdslAnswerDict:
|
63
|
+
"""This is for testing purposes. A question can be given an exception to throw or an answer to always return.
|
64
|
+
|
65
|
+
>>> rv = ResponseValidatorABC.example()
|
66
|
+
>>> rv.override_answer = {"answer": 42}
|
67
|
+
>>> rv.validate({"answer": 23})
|
68
|
+
{'answer': 42, 'comment': None, 'generated_tokens': None}
|
69
|
+
"""
|
70
|
+
if self.exception_to_throw:
|
71
|
+
raise self.exception_to_throw
|
72
|
+
return self.override_answer if self.override_answer else data
|
73
|
+
|
74
|
+
def _base_validate(self, data: RawEdslAnswerDict) -> BaseModel:
|
75
|
+
"""This is the main validation function. It takes the response_model and checks the data against it, returning the instantiated model.
|
76
|
+
|
77
|
+
>>> rv = ResponseValidatorABC.example("numerical")
|
78
|
+
>>> rv._base_validate({"answer": 42})
|
79
|
+
ConstrainedNumericResponse(answer=42, comment=None, generated_tokens=None)
|
80
|
+
"""
|
81
|
+
try:
|
82
|
+
return self.response_model(**data)
|
83
|
+
except ValidationError as e:
|
84
|
+
raise QuestionAnswerValidationError(e, data=data, model=self.response_model)
|
85
|
+
|
86
|
+
def post_validation_answer_convert(self, data):
|
87
|
+
return data
|
88
|
+
|
89
|
+
class EdslAnswerDict(TypedDict):
|
90
|
+
answer: Any
|
91
|
+
comment: Optional[str]
|
92
|
+
generated_tokens: Optional[str]
|
93
|
+
|
94
|
+
def validate(
|
95
|
+
self,
|
96
|
+
raw_edsl_answer_dict: RawEdslAnswerDict,
|
97
|
+
fix=False,
|
98
|
+
verbose=False,
|
99
|
+
replacement_dict: dict = None,
|
100
|
+
) -> EdslAnswerDict:
|
101
|
+
"""This is the main validation function.
|
102
|
+
|
103
|
+
>>> rv = ResponseValidatorABC.example("numerical")
|
104
|
+
>>> rv.validate({"answer": 42})
|
105
|
+
{'answer': 42, 'comment': None, 'generated_tokens': None}
|
106
|
+
>>> rv.max_value
|
107
|
+
86.7
|
108
|
+
>>> rv.validate({"answer": "120"})
|
109
|
+
Traceback (most recent call last):
|
110
|
+
...
|
111
|
+
edsl.exceptions.questions.QuestionAnswerValidationError:...
|
112
|
+
>>> from edsl import QuestionNumerical
|
113
|
+
>>> q = QuestionNumerical.example()
|
114
|
+
>>> q.permissive = True
|
115
|
+
>>> rv = q.response_validator
|
116
|
+
>>> rv.validate({"answer": "120"})
|
117
|
+
{'answer': 120, 'comment': None, 'generated_tokens': None}
|
118
|
+
>>> rv.validate({"answer": "poo"})
|
119
|
+
Traceback (most recent call last):
|
120
|
+
...
|
121
|
+
edsl.exceptions.questions.QuestionAnswerValidationError:...
|
122
|
+
"""
|
123
|
+
proposed_edsl_answer_dict = self._preprocess(raw_edsl_answer_dict)
|
124
|
+
try:
|
125
|
+
pydantic_edsl_answer: BaseModel = self._base_validate(
|
126
|
+
proposed_edsl_answer_dict
|
127
|
+
)
|
128
|
+
edsl_answer_dict = self._extract_answer(pydantic_edsl_answer)
|
129
|
+
return self._post_process(edsl_answer_dict)
|
130
|
+
except QuestionAnswerValidationError as e:
|
131
|
+
if verbose:
|
132
|
+
print(f"Failed to validate {raw_edsl_answer_dict}; {str(e)}")
|
133
|
+
return self._handle_exception(e, raw_edsl_answer_dict)
|
134
|
+
|
135
|
+
def _handle_exception(self, e: Exception, raw_edsl_answer_dict) -> EdslAnswerDict:
|
136
|
+
if self.fixes_tried == 0:
|
137
|
+
self.original_exception = e
|
138
|
+
|
139
|
+
if self.fixes_tried == 0 and hasattr(self, "fix"):
|
140
|
+
self.fixes_tried += 1
|
141
|
+
fixed_data = self.fix(raw_edsl_answer_dict)
|
142
|
+
try:
|
143
|
+
return self.validate(fixed_data, fix=True)
|
144
|
+
except Exception as e:
|
145
|
+
pass # we don't log failed fixes
|
146
|
+
|
147
|
+
raise QuestionAnswerValidationError(
|
148
|
+
self.original_exception,
|
149
|
+
data=raw_edsl_answer_dict,
|
150
|
+
model=self.response_model,
|
151
|
+
)
|
152
|
+
|
153
|
+
def _check_constraints(self, pydantic_edsl_answer: BaseModel) -> dict:
|
154
|
+
pass
|
155
|
+
|
156
|
+
def _extract_answer(self, response: BaseModel) -> EdslAnswerDict:
|
157
|
+
return response.model_dump()
|
158
|
+
|
159
|
+
def _post_process(self, edsl_answer_dict: EdslAnswerDict) -> EdslAnswerDict:
|
160
|
+
return edsl_answer_dict
|
161
|
+
|
162
|
+
@classmethod
|
163
|
+
def example(cls, question_type="numerical"):
|
164
|
+
from edsl import Question
|
165
|
+
|
166
|
+
q = Question.example(question_type)
|
167
|
+
return q.response_validator
|
168
|
+
|
169
|
+
|
170
|
+
# Example usage
|
171
|
+
if __name__ == "__main__":
|
172
|
+
import doctest
|
173
|
+
|
174
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|