edsl 0.1.39__py3-none-any.whl → 0.1.39.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 +116 -197
- edsl/__init__.py +7 -15
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +147 -351
- edsl/agents/AgentList.py +73 -211
- edsl/agents/Invigilator.py +50 -101
- edsl/agents/InvigilatorBase.py +70 -62
- edsl/agents/PromptConstructor.py +225 -143
- edsl/agents/__init__.py +1 -0
- edsl/agents/prompt_helpers.py +3 -3
- edsl/auto/AutoStudy.py +5 -18
- edsl/auto/StageBase.py +40 -53
- edsl/auto/StageQuestions.py +1 -2
- edsl/auto/utilities.py +6 -0
- edsl/config.py +2 -22
- edsl/conversation/car_buying.py +1 -2
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +47 -125
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +27 -45
- edsl/data/CacheEntry.py +15 -12
- edsl/data/CacheHandler.py +12 -31
- edsl/data/RemoteCacheSync.py +46 -154
- edsl/data/__init__.py +3 -4
- edsl/data_transfer_models.py +1 -2
- edsl/enums.py +0 -27
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +0 -12
- edsl/exceptions/questions.py +6 -24
- edsl/exceptions/scenarios.py +0 -7
- edsl/inference_services/AnthropicService.py +19 -38
- edsl/inference_services/AwsBedrock.py +2 -0
- edsl/inference_services/AzureAI.py +2 -0
- edsl/inference_services/GoogleService.py +12 -7
- edsl/inference_services/InferenceServiceABC.py +85 -18
- edsl/inference_services/InferenceServicesCollection.py +79 -120
- edsl/inference_services/MistralAIService.py +3 -0
- edsl/inference_services/OpenAIService.py +35 -47
- edsl/inference_services/PerplexityService.py +3 -0
- edsl/inference_services/TestService.py +10 -11
- edsl/inference_services/TogetherAIService.py +3 -5
- edsl/jobs/Answers.py +14 -1
- edsl/jobs/Jobs.py +431 -356
- edsl/jobs/JobsChecks.py +10 -35
- edsl/jobs/JobsPrompts.py +4 -6
- edsl/jobs/JobsRemoteInferenceHandler.py +133 -205
- edsl/jobs/buckets/BucketCollection.py +3 -44
- edsl/jobs/buckets/TokenBucket.py +21 -53
- edsl/jobs/interviews/Interview.py +408 -143
- edsl/jobs/runners/JobsRunnerAsyncio.py +403 -88
- edsl/jobs/runners/JobsRunnerStatus.py +165 -133
- edsl/jobs/tasks/QuestionTaskCreator.py +19 -21
- edsl/jobs/tasks/TaskHistory.py +18 -38
- edsl/jobs/tasks/task_status_enum.py +2 -0
- edsl/language_models/KeyLookup.py +30 -0
- edsl/language_models/LanguageModel.py +236 -194
- edsl/language_models/ModelList.py +19 -28
- edsl/language_models/__init__.py +2 -1
- edsl/language_models/registry.py +190 -0
- edsl/language_models/repair.py +2 -2
- edsl/language_models/unused/ReplicateBase.py +83 -0
- edsl/language_models/utilities.py +4 -5
- edsl/notebooks/Notebook.py +14 -19
- edsl/prompts/Prompt.py +39 -29
- edsl/questions/{answer_validator_mixin.py → AnswerValidatorMixin.py} +2 -47
- edsl/questions/QuestionBase.py +214 -68
- edsl/questions/{question_base_gen_mixin.py → QuestionBaseGenMixin.py} +50 -57
- edsl/questions/QuestionBasePromptsMixin.py +3 -7
- edsl/questions/QuestionBudget.py +1 -1
- edsl/questions/QuestionCheckBox.py +3 -3
- edsl/questions/QuestionExtract.py +7 -5
- edsl/questions/QuestionFreeText.py +3 -2
- edsl/questions/QuestionList.py +18 -10
- edsl/questions/QuestionMultipleChoice.py +23 -67
- edsl/questions/QuestionNumerical.py +4 -2
- edsl/questions/QuestionRank.py +17 -7
- edsl/questions/{response_validator_abc.py → ResponseValidatorABC.py} +26 -40
- edsl/questions/SimpleAskMixin.py +3 -4
- edsl/questions/__init__.py +1 -2
- edsl/questions/derived/QuestionLinearScale.py +3 -6
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +3 -17
- edsl/questions/question_registry.py +1 -1
- edsl/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +7 -170
- edsl/results/DatasetExportMixin.py +305 -168
- edsl/results/DatasetTree.py +8 -28
- edsl/results/Result.py +206 -298
- edsl/results/Results.py +131 -149
- edsl/results/ResultsDBMixin.py +238 -0
- edsl/results/ResultsExportMixin.py +0 -2
- edsl/results/{results_selector.py → Selector.py} +13 -23
- edsl/results/TableDisplay.py +171 -98
- edsl/results/__init__.py +1 -1
- edsl/scenarios/FileStore.py +239 -150
- edsl/scenarios/Scenario.py +193 -90
- edsl/scenarios/ScenarioHtmlMixin.py +3 -4
- edsl/scenarios/{scenario_join.py → ScenarioJoin.py} +6 -10
- edsl/scenarios/ScenarioList.py +244 -415
- edsl/scenarios/ScenarioListExportMixin.py +7 -0
- edsl/scenarios/ScenarioListPdfMixin.py +37 -15
- edsl/scenarios/__init__.py +2 -1
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +12 -5
- edsl/surveys/Rule.py +4 -5
- edsl/surveys/RuleCollection.py +27 -25
- edsl/surveys/Survey.py +791 -270
- edsl/surveys/SurveyCSS.py +8 -20
- edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +9 -11
- edsl/surveys/__init__.py +2 -4
- edsl/surveys/descriptors.py +2 -6
- edsl/surveys/instructions/ChangeInstruction.py +2 -1
- edsl/surveys/instructions/Instruction.py +13 -4
- edsl/surveys/instructions/InstructionCollection.py +6 -11
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/utilities.py +23 -35
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/METADATA +10 -12
- edsl-0.1.39.dev1.dist-info/RECORD +277 -0
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/WHEEL +1 -1
- edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
- edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
- edsl/agents/question_option_processor.py +0 -172
- edsl/coop/CoopFunctionsMixin.py +0 -15
- edsl/coop/ExpectedParrotKeyHandler.py +0 -125
- edsl/exceptions/inference_services.py +0 -5
- edsl/inference_services/AvailableModelCacheHandler.py +0 -184
- edsl/inference_services/AvailableModelFetcher.py +0 -215
- edsl/inference_services/ServiceAvailability.py +0 -135
- edsl/inference_services/data_structures.py +0 -134
- edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -223
- edsl/jobs/FetchInvigilator.py +0 -47
- edsl/jobs/InterviewTaskManager.py +0 -98
- edsl/jobs/InterviewsConstructor.py +0 -50
- edsl/jobs/JobsComponentConstructor.py +0 -189
- edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
- edsl/jobs/RequestTokenEstimator.py +0 -30
- edsl/jobs/async_interview_runner.py +0 -138
- edsl/jobs/buckets/TokenBucketAPI.py +0 -211
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/check_survey_scenario_compatibility.py +0 -85
- edsl/jobs/data_structures.py +0 -120
- edsl/jobs/decorators.py +0 -35
- edsl/jobs/jobs_status_enums.py +0 -9
- edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
- edsl/jobs/results_exceptions_handler.py +0 -98
- edsl/language_models/ComputeCost.py +0 -63
- edsl/language_models/PriceManager.py +0 -127
- edsl/language_models/RawResponseHandler.py +0 -106
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +0 -131
- edsl/language_models/model.py +0 -256
- edsl/notebooks/NotebookToLaTeX.py +0 -142
- edsl/questions/ExceptionExplainer.py +0 -77
- edsl/questions/HTMLQuestion.py +0 -103
- edsl/questions/QuestionMatrix.py +0 -265
- edsl/questions/data_structures.py +0 -20
- edsl/questions/loop_processor.py +0 -149
- edsl/questions/response_validator_factory.py +0 -34
- edsl/questions/templates/matrix/__init__.py +0 -1
- edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
- edsl/questions/templates/matrix/question_presentation.jinja +0 -20
- edsl/results/MarkdownToDocx.py +0 -122
- edsl/results/MarkdownToPDF.py +0 -111
- edsl/results/TextEditor.py +0 -50
- edsl/results/file_exports.py +0 -252
- edsl/results/smart_objects.py +0 -96
- edsl/results/table_data_class.py +0 -12
- edsl/results/table_renderers.py +0 -118
- edsl/scenarios/ConstructDownloadLink.py +0 -109
- edsl/scenarios/DocumentChunker.py +0 -102
- edsl/scenarios/DocxScenario.py +0 -16
- edsl/scenarios/PdfExtractor.py +0 -40
- edsl/scenarios/directory_scanner.py +0 -96
- edsl/scenarios/file_methods.py +0 -85
- edsl/scenarios/handlers/__init__.py +0 -13
- edsl/scenarios/handlers/csv.py +0 -49
- edsl/scenarios/handlers/docx.py +0 -76
- edsl/scenarios/handlers/html.py +0 -37
- edsl/scenarios/handlers/json.py +0 -111
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/scenarios/handlers/md.py +0 -51
- edsl/scenarios/handlers/pdf.py +0 -68
- edsl/scenarios/handlers/png.py +0 -39
- edsl/scenarios/handlers/pptx.py +0 -105
- edsl/scenarios/handlers/py.py +0 -294
- edsl/scenarios/handlers/sql.py +0 -313
- edsl/scenarios/handlers/sqlite.py +0 -149
- edsl/scenarios/handlers/txt.py +0 -33
- edsl/scenarios/scenario_selector.py +0 -156
- edsl/surveys/ConstructDAG.py +0 -92
- edsl/surveys/EditSurvey.py +0 -221
- edsl/surveys/InstructionHandler.py +0 -100
- edsl/surveys/MemoryManagement.py +0 -72
- edsl/surveys/RuleManager.py +0 -172
- edsl/surveys/Simulator.py +0 -75
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/utilities/PrettyList.py +0 -56
- edsl/utilities/is_notebook.py +0 -18
- edsl/utilities/is_valid_variable_name.py +0 -11
- edsl/utilities/remove_edsl_version.py +0 -24
- edsl-0.1.39.dist-info/RECORD +0 -358
- /edsl/questions/{register_questions_meta.py → RegisterQuestionsMeta.py} +0 -0
- /edsl/results/{results_fetch_mixin.py → ResultsFetchMixin.py} +0 -0
- /edsl/results/{results_tools_mixin.py → ResultsToolsMixin.py} +0 -0
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/LICENSE +0 -0
edsl/questions/QuestionBudget.py
CHANGED
@@ -5,7 +5,7 @@ from pydantic import Field, BaseModel, validator
|
|
5
5
|
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
7
7
|
from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
|
8
|
-
from edsl.questions.
|
8
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
9
9
|
|
10
10
|
|
11
11
|
class BudgewResponseValidator(ResponseValidatorABC):
|
@@ -13,10 +13,10 @@ from edsl.questions.descriptors import (
|
|
13
13
|
from edsl.questions.decorators import inject_exception
|
14
14
|
|
15
15
|
from pydantic import field_validator
|
16
|
-
from edsl.questions.
|
17
|
-
from edsl.questions.
|
16
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
17
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
18
18
|
|
19
|
-
from edsl.exceptions
|
19
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
20
20
|
|
21
21
|
from pydantic import BaseModel, Field, conlist
|
22
22
|
from typing import List, Literal, Optional, Annotated
|
@@ -6,8 +6,9 @@ from typing import Any, Optional, Dict
|
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
7
7
|
from edsl.questions.descriptors import AnswerTemplateDescriptor
|
8
8
|
|
9
|
-
from edsl.questions.
|
10
|
-
from edsl.questions.
|
9
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
10
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
11
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
11
12
|
from edsl.questions.decorators import inject_exception
|
12
13
|
|
13
14
|
from typing import Dict, Any
|
@@ -56,7 +57,7 @@ def dict_to_pydantic_model(input_dict: Dict[str, Any]) -> Any:
|
|
56
57
|
DynamicModel = create_model("DynamicModel", **field_definitions)
|
57
58
|
|
58
59
|
class AnswerModel(BaseResponse):
|
59
|
-
answer:
|
60
|
+
answer: DynamicModel
|
60
61
|
generated_tokens: Optional[str] = None
|
61
62
|
comment: Optional[str] = None
|
62
63
|
|
@@ -112,8 +113,6 @@ class QuestionExtract(QuestionBase):
|
|
112
113
|
:param question_name: The name of the question.
|
113
114
|
:param question_text: The text of the question.
|
114
115
|
:param answer_template: The template for the answer.
|
115
|
-
:param answering_instructions: Instructions for answering the question.
|
116
|
-
:param question_presentation: The presentation of the question.
|
117
116
|
"""
|
118
117
|
self.question_name = question_name
|
119
118
|
self.question_text = question_text
|
@@ -143,6 +142,9 @@ class QuestionExtract(QuestionBase):
|
|
143
142
|
)
|
144
143
|
return question_html_content
|
145
144
|
|
145
|
+
################
|
146
|
+
# Helpful methods
|
147
|
+
################
|
146
148
|
@classmethod
|
147
149
|
@inject_exception
|
148
150
|
def example(cls) -> QuestionExtract:
|
@@ -5,14 +5,15 @@ from uuid import uuid4
|
|
5
5
|
from pydantic import field_validator
|
6
6
|
|
7
7
|
from edsl.questions.QuestionBase import QuestionBase
|
8
|
-
from edsl.questions.
|
8
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
9
9
|
|
10
|
-
from edsl.exceptions
|
10
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
11
11
|
from edsl.questions.decorators import inject_exception
|
12
12
|
|
13
13
|
from pydantic import BaseModel
|
14
14
|
from typing import Optional, Any, List
|
15
15
|
|
16
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
16
17
|
from edsl.prompts.Prompt import Prompt
|
17
18
|
|
18
19
|
|
edsl/questions/QuestionList.py
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import
|
2
|
+
import random
|
3
|
+
import textwrap
|
3
4
|
from typing import Any, Optional, Union
|
4
|
-
|
5
|
-
from pydantic import Field
|
6
|
-
from json_repair import repair_json
|
7
|
-
|
8
|
-
from edsl.exceptions.questions import QuestionAnswerValidationError
|
9
5
|
from edsl.questions.QuestionBase import QuestionBase
|
10
6
|
from edsl.questions.descriptors import IntegerOrNoneDescriptor
|
11
7
|
from edsl.questions.decorators import inject_exception
|
12
|
-
from edsl.questions.response_validator_abc import ResponseValidatorABC
|
13
8
|
|
9
|
+
from pydantic import field_validator, Field
|
10
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
11
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
12
|
+
|
13
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
14
|
+
import textwrap
|
15
|
+
import json
|
14
16
|
|
15
|
-
|
17
|
+
from json_repair import repair_json
|
18
|
+
|
19
|
+
|
20
|
+
def convert_string(s):
|
16
21
|
"""Convert a string to a more appropriate type if possible.
|
17
22
|
|
18
23
|
>>> convert_string("3.14")
|
@@ -55,7 +60,7 @@ def convert_string(s: str) -> Union[float, int, str, dict]:
|
|
55
60
|
return s
|
56
61
|
|
57
62
|
|
58
|
-
def create_model(max_list_items: int, permissive
|
63
|
+
def create_model(max_list_items: int, permissive):
|
59
64
|
from pydantic import BaseModel
|
60
65
|
|
61
66
|
if permissive or max_list_items is None:
|
@@ -130,8 +135,8 @@ class QuestionList(QuestionBase):
|
|
130
135
|
self,
|
131
136
|
question_name: str,
|
132
137
|
question_text: str,
|
133
|
-
include_comment: bool = True,
|
134
138
|
max_list_items: Optional[int] = None,
|
139
|
+
include_comment: bool = True,
|
135
140
|
answering_instructions: Optional[str] = None,
|
136
141
|
question_presentation: Optional[str] = None,
|
137
142
|
permissive: bool = False,
|
@@ -181,6 +186,9 @@ class QuestionList(QuestionBase):
|
|
181
186
|
).render(question_name=self.question_name)
|
182
187
|
return question_html_content
|
183
188
|
|
189
|
+
################
|
190
|
+
# Helpful methods
|
191
|
+
################
|
184
192
|
@classmethod
|
185
193
|
@inject_exception
|
186
194
|
def example(
|
@@ -8,7 +8,7 @@ from edsl.scenarios.Scenario import Scenario
|
|
8
8
|
from edsl.questions.QuestionBase import QuestionBase
|
9
9
|
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
10
10
|
from edsl.questions.decorators import inject_exception
|
11
|
-
from edsl.questions.
|
11
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
12
12
|
|
13
13
|
|
14
14
|
def create_response_model(choices: List[str], permissive: bool = False):
|
@@ -120,9 +120,9 @@ class QuestionMultipleChoice(QuestionBase):
|
|
120
120
|
|
121
121
|
question_type = "multiple_choice"
|
122
122
|
purpose = "When options are known and limited"
|
123
|
-
question_options: Union[
|
124
|
-
|
125
|
-
)
|
123
|
+
question_options: Union[
|
124
|
+
list[str], list[list], list[float], list[int]
|
125
|
+
] = QuestionOptionsDescriptor()
|
126
126
|
_response_model = None
|
127
127
|
response_validator_class = MultipleChoiceResponseValidator
|
128
128
|
|
@@ -175,54 +175,8 @@ class QuestionMultipleChoice(QuestionBase):
|
|
175
175
|
else:
|
176
176
|
return create_response_model(self.question_options, self.permissive)
|
177
177
|
|
178
|
-
@staticmethod
|
179
|
-
def _translate_question_options(
|
180
|
-
question_options, substitution_dict: dict
|
181
|
-
) -> list[str]:
|
182
|
-
|
183
|
-
if isinstance(question_options, str):
|
184
|
-
# If dynamic options are provided like {{ options }}, render them with the scenario
|
185
|
-
# We can check if it's in the Scenario.
|
186
|
-
from jinja2 import Environment, meta
|
187
|
-
|
188
|
-
env = Environment()
|
189
|
-
parsed_content = env.parse(question_options)
|
190
|
-
template_variables = list(meta.find_undeclared_variables(parsed_content))
|
191
|
-
# print("The template variables are: ", template_variables)
|
192
|
-
question_option_key = template_variables[0]
|
193
|
-
# We need to deal with possibility it's actually an answer to a question.
|
194
|
-
potential_replacement = substitution_dict.get(question_option_key, None)
|
195
|
-
|
196
|
-
if isinstance(potential_replacement, list):
|
197
|
-
# translated_options = potential_replacement
|
198
|
-
return potential_replacement
|
199
|
-
|
200
|
-
if isinstance(potential_replacement, QuestionBase):
|
201
|
-
if hasattr(potential_replacement, "answer") and isinstance(
|
202
|
-
potential_replacement.answer, list
|
203
|
-
):
|
204
|
-
return potential_replacement.answer
|
205
|
-
# translated_options = potential_replacement.answer
|
206
|
-
|
207
|
-
# if not isinstance(potential_replacement, list):
|
208
|
-
# translated_options = potential_replacement
|
209
|
-
|
210
|
-
if potential_replacement is None:
|
211
|
-
# Nope - maybe it's in the substition dict?
|
212
|
-
raise ValueError(
|
213
|
-
f"Could not find the key '{question_option_key}' in the scenario."
|
214
|
-
f"The substition dict was: '{substitution_dict}.'"
|
215
|
-
f"The question options were: '{question_options}'."
|
216
|
-
)
|
217
|
-
else:
|
218
|
-
translated_options = [
|
219
|
-
Template(str(option)).render(substitution_dict)
|
220
|
-
for option in question_options
|
221
|
-
]
|
222
|
-
return translated_options
|
223
|
-
|
224
178
|
def _translate_answer_code_to_answer(
|
225
|
-
self, answer_code: int,
|
179
|
+
self, answer_code: int, scenario: Optional["Scenario"] = None
|
226
180
|
):
|
227
181
|
"""Translate the answer code to the actual answer.
|
228
182
|
|
@@ -238,24 +192,26 @@ class QuestionMultipleChoice(QuestionBase):
|
|
238
192
|
'Happy'
|
239
193
|
|
240
194
|
"""
|
241
|
-
if replacements_dict is None:
|
242
|
-
replacements_dict = {}
|
243
|
-
translated_options = self._translate_question_options(
|
244
|
-
self.question_options, replacements_dict
|
245
|
-
)
|
246
195
|
|
196
|
+
scenario = scenario or Scenario()
|
197
|
+
|
198
|
+
if isinstance(self.question_options, str):
|
199
|
+
# If dynamic options are provided like {{ options }}, render them with the scenario
|
200
|
+
from jinja2 import Environment, meta
|
201
|
+
|
202
|
+
env = Environment()
|
203
|
+
parsed_content = env.parse(self.question_options)
|
204
|
+
question_option_key = list(meta.find_undeclared_variables(parsed_content))[
|
205
|
+
0
|
206
|
+
]
|
207
|
+
translated_options = scenario.get(question_option_key)
|
208
|
+
else:
|
209
|
+
translated_options = [
|
210
|
+
Template(str(option)).render(scenario)
|
211
|
+
for option in self.question_options
|
212
|
+
]
|
247
213
|
if self._use_code:
|
248
|
-
|
249
|
-
return translated_options[int(answer_code)]
|
250
|
-
except IndexError:
|
251
|
-
raise ValueError(
|
252
|
-
f"Answer code is out of range. The answer code index was: {int(answer_code)}. The options were: {translated_options}."
|
253
|
-
)
|
254
|
-
except TypeError:
|
255
|
-
raise ValueError(
|
256
|
-
f"The answer code was: '{answer_code}.'",
|
257
|
-
f"The options were: '{translated_options}'.",
|
258
|
-
)
|
214
|
+
return translated_options[int(answer_code)]
|
259
215
|
else:
|
260
216
|
# return translated_options[answer_code]
|
261
217
|
return answer_code
|
@@ -1,15 +1,17 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
# from decimal import Decimal
|
3
4
|
from random import uniform
|
4
5
|
from typing import Any, Optional, Union, Literal
|
5
6
|
|
6
7
|
from pydantic import BaseModel, Field, field_validator
|
7
8
|
|
8
|
-
from edsl.exceptions
|
9
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
9
10
|
from edsl.questions.QuestionBase import QuestionBase
|
10
11
|
from edsl.questions.descriptors import NumericalOrNoneDescriptor
|
11
12
|
from edsl.questions.decorators import inject_exception
|
12
|
-
from edsl.questions.
|
13
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
14
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
13
15
|
|
14
16
|
|
15
17
|
def create_numeric_response(
|
edsl/questions/QuestionRank.py
CHANGED
@@ -1,14 +1,25 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
|
3
|
-
|
4
|
-
from
|
5
|
-
|
2
|
+
import random
|
3
|
+
import textwrap
|
4
|
+
from jinja2 import Template
|
5
|
+
from typing import Any, Optional, Union
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
7
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
+
|
7
9
|
from edsl.questions.descriptors import (
|
8
10
|
QuestionOptionsDescriptor,
|
9
11
|
NumSelectionsDescriptor,
|
10
12
|
)
|
11
|
-
|
13
|
+
|
14
|
+
from edsl.prompts import Prompt
|
15
|
+
|
16
|
+
from pydantic import field_validator
|
17
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
18
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
19
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
20
|
+
|
21
|
+
from pydantic import BaseModel, Field, create_model
|
22
|
+
from typing import Optional, Any, List, Annotated, Literal
|
12
23
|
|
13
24
|
|
14
25
|
def create_response_model(
|
@@ -190,8 +201,7 @@ class QuestionRank(QuestionBase):
|
|
190
201
|
self, answer_codes, scenario: Scenario = None
|
191
202
|
) -> list[str]:
|
192
203
|
"""Translate the answer code to the actual answer."""
|
193
|
-
from edsl.scenarios
|
194
|
-
from jinja2 import Template
|
204
|
+
from edsl.scenarios import Scenario
|
195
205
|
|
196
206
|
scenario = scenario or Scenario()
|
197
207
|
translated_options = [
|
@@ -1,22 +1,23 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
|
+
from pydantic import BaseModel, Field, field_validator
|
3
|
+
|
4
|
+
# from decimal import Decimal
|
2
5
|
from typing import Optional, Any, List, TypedDict
|
3
6
|
|
4
|
-
from
|
7
|
+
from edsl.exceptions import QuestionAnswerValidationError
|
8
|
+
from pydantic import ValidationError
|
5
9
|
|
6
|
-
from edsl.exceptions.questions import QuestionAnswerValidationError
|
7
|
-
from edsl.questions.ExceptionExplainer import ExceptionExplainer
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
class BaseResponse(BaseModel):
|
12
|
+
answer: Any
|
13
|
+
comment: Optional[str] = None
|
14
|
+
generated_tokens: Optional[str] = None
|
13
15
|
|
14
16
|
|
15
17
|
class ResponseValidatorABC(ABC):
|
16
18
|
required_params: List[str] = []
|
17
19
|
|
18
20
|
def __init_subclass__(cls, **kwargs):
|
19
|
-
"""This is a metaclass that ensures that all subclasses of ResponseValidatorABC have the required class variables."""
|
20
21
|
super().__init_subclass__(**kwargs)
|
21
22
|
required_class_vars = ["required_params", "valid_examples", "invalid_examples"]
|
22
23
|
for var in required_class_vars:
|
@@ -51,7 +52,12 @@ class ResponseValidatorABC(ABC):
|
|
51
52
|
if not hasattr(self, "permissive"):
|
52
53
|
self.permissive = False
|
53
54
|
|
54
|
-
self.fixes_tried = 0
|
55
|
+
self.fixes_tried = 0
|
56
|
+
|
57
|
+
class RawEdslAnswerDict(TypedDict):
|
58
|
+
answer: Any
|
59
|
+
comment: Optional[str]
|
60
|
+
generated_tokens: Optional[str]
|
55
61
|
|
56
62
|
def _preprocess(self, data: RawEdslAnswerDict) -> RawEdslAnswerDict:
|
57
63
|
"""This is for testing purposes. A question can be given an exception to throw or an answer to always return.
|
@@ -66,8 +72,7 @@ class ResponseValidatorABC(ABC):
|
|
66
72
|
return self.override_answer if self.override_answer else data
|
67
73
|
|
68
74
|
def _base_validate(self, data: RawEdslAnswerDict) -> BaseModel:
|
69
|
-
"""This is the main validation function. It takes the response_model and checks the data against it,
|
70
|
-
returning the instantiated model.
|
75
|
+
"""This is the main validation function. It takes the response_model and checks the data against it, returning the instantiated model.
|
71
76
|
|
72
77
|
>>> rv = ResponseValidatorABC.example("numerical")
|
73
78
|
>>> rv._base_validate({"answer": 42})
|
@@ -76,13 +81,16 @@ class ResponseValidatorABC(ABC):
|
|
76
81
|
try:
|
77
82
|
return self.response_model(**data)
|
78
83
|
except ValidationError as e:
|
79
|
-
raise QuestionAnswerValidationError(
|
80
|
-
message=str(e), pydantic_error=e, data=data, model=self.response_model
|
81
|
-
)
|
84
|
+
raise QuestionAnswerValidationError(e, data=data, model=self.response_model)
|
82
85
|
|
83
86
|
def post_validation_answer_convert(self, data):
|
84
87
|
return data
|
85
88
|
|
89
|
+
class EdslAnswerDict(TypedDict):
|
90
|
+
answer: Any
|
91
|
+
comment: Optional[str]
|
92
|
+
generated_tokens: Optional[str]
|
93
|
+
|
86
94
|
def validate(
|
87
95
|
self,
|
88
96
|
raw_edsl_answer_dict: RawEdslAnswerDict,
|
@@ -120,12 +128,10 @@ class ResponseValidatorABC(ABC):
|
|
120
128
|
edsl_answer_dict = self._extract_answer(pydantic_edsl_answer)
|
121
129
|
return self._post_process(edsl_answer_dict)
|
122
130
|
except QuestionAnswerValidationError as e:
|
131
|
+
if verbose:
|
132
|
+
print(f"Failed to validate {raw_edsl_answer_dict}; {str(e)}")
|
123
133
|
return self._handle_exception(e, raw_edsl_answer_dict)
|
124
134
|
|
125
|
-
def human_explanation(self, e: QuestionAnswerValidationError):
|
126
|
-
explanation = ExceptionExplainer(e, model_response=e.data).explain()
|
127
|
-
return explanation
|
128
|
-
|
129
135
|
def _handle_exception(self, e: Exception, raw_edsl_answer_dict) -> EdslAnswerDict:
|
130
136
|
if self.fixes_tried == 0:
|
131
137
|
self.original_exception = e
|
@@ -134,18 +140,12 @@ class ResponseValidatorABC(ABC):
|
|
134
140
|
self.fixes_tried += 1
|
135
141
|
fixed_data = self.fix(raw_edsl_answer_dict)
|
136
142
|
try:
|
137
|
-
return self.validate(fixed_data, fix=True)
|
143
|
+
return self.validate(fixed_data, fix=True)
|
138
144
|
except Exception as e:
|
139
145
|
pass # we don't log failed fixes
|
140
146
|
|
141
|
-
# If the exception is already a QuestionAnswerValidationError, raise it
|
142
|
-
if isinstance(self.original_exception, QuestionAnswerValidationError):
|
143
|
-
raise self.original_exception
|
144
|
-
|
145
|
-
# If nothing worked, raise the original exception
|
146
147
|
raise QuestionAnswerValidationError(
|
147
|
-
|
148
|
-
pydantic_error=self.original_exception,
|
148
|
+
self.original_exception,
|
149
149
|
data=raw_edsl_answer_dict,
|
150
150
|
model=self.response_model,
|
151
151
|
)
|
@@ -167,22 +167,8 @@ class ResponseValidatorABC(ABC):
|
|
167
167
|
return q.response_validator
|
168
168
|
|
169
169
|
|
170
|
-
def main():
|
171
|
-
rv = ResponseValidatorABC.example()
|
172
|
-
print(rv.validate({"answer": 42}))
|
173
|
-
|
174
|
-
|
175
170
|
# Example usage
|
176
171
|
if __name__ == "__main__":
|
177
172
|
import doctest
|
178
173
|
|
179
174
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
180
|
-
|
181
|
-
rv = ResponseValidatorABC.example()
|
182
|
-
# print(rv.validate({"answer": 42}))
|
183
|
-
|
184
|
-
rv = ResponseValidatorABC.example()
|
185
|
-
try:
|
186
|
-
rv.validate({"answer": "120"})
|
187
|
-
except QuestionAnswerValidationError as e:
|
188
|
-
print(rv.human_explanation(e))
|
edsl/questions/SimpleAskMixin.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from rich.table import Table
|
2
|
+
from rich.console import Console
|
1
3
|
import math
|
2
4
|
|
3
5
|
|
@@ -8,9 +10,6 @@ def logprob_to_prob(logprob):
|
|
8
10
|
|
9
11
|
|
10
12
|
def format_output(data):
|
11
|
-
from rich.table import Table
|
12
|
-
from rich.console import Console
|
13
|
-
|
14
13
|
content = data["choices"][0]["logprobs"]["content"]
|
15
14
|
table = Table(show_header=True, header_style="bold magenta")
|
16
15
|
|
@@ -66,7 +65,7 @@ class SimpleAskMixin:
|
|
66
65
|
system_prompt="You are a helpful agent pretending to be a human. Do not break character",
|
67
66
|
top_logprobs=4,
|
68
67
|
):
|
69
|
-
from edsl
|
68
|
+
from edsl import Model
|
70
69
|
|
71
70
|
if model is None:
|
72
71
|
model = Model()
|
edsl/questions/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Schemas
|
2
2
|
from edsl.questions.settings import Settings
|
3
|
-
from edsl.questions.
|
3
|
+
from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
|
4
4
|
|
5
5
|
# Base Class
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
@@ -11,7 +11,6 @@ from edsl.questions.QuestionExtract import QuestionExtract
|
|
11
11
|
from edsl.questions.QuestionFreeText import QuestionFreeText
|
12
12
|
from edsl.questions.QuestionFunctional import QuestionFunctional
|
13
13
|
from edsl.questions.QuestionList import QuestionList
|
14
|
-
from edsl.questions.QuestionMatrix import QuestionMatrix
|
15
14
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
16
15
|
from edsl.questions.QuestionNumerical import QuestionNumerical
|
17
16
|
from edsl.questions.QuestionBudget import QuestionBudget
|
@@ -40,12 +40,9 @@ class QuestionLinearScale(QuestionMultipleChoice):
|
|
40
40
|
include_comment=include_comment,
|
41
41
|
)
|
42
42
|
self.question_options = question_options
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.option_labels = (
|
47
|
-
{int(k): v for k, v in option_labels.items()} if option_labels else {}
|
48
|
-
)
|
43
|
+
self.option_labels = (
|
44
|
+
{int(k): v for k, v in option_labels.items()} if option_labels else {}
|
45
|
+
)
|
49
46
|
self.answering_instructions = answering_instructions
|
50
47
|
self.question_presentation = question_presentation
|
51
48
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Optional
|
3
3
|
|
4
|
-
from edsl.exceptions
|
4
|
+
from edsl.exceptions import QuestionCreationValidationError
|
5
5
|
from edsl.questions.QuestionCheckBox import QuestionCheckBox
|
6
6
|
from edsl.questions.decorators import inject_exception
|
7
7
|
|
edsl/questions/descriptors.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
from abc import ABC, abstractmethod
|
4
4
|
import re
|
5
5
|
from typing import Any, Callable, List, Optional
|
6
|
-
from edsl.exceptions
|
6
|
+
from edsl.exceptions import (
|
7
7
|
QuestionCreationValidationError,
|
8
8
|
QuestionAnswerValidationError,
|
9
9
|
)
|
@@ -181,25 +181,11 @@ class NumSelectionsDescriptor(BaseDescriptor):
|
|
181
181
|
|
182
182
|
|
183
183
|
class OptionLabelDescriptor(BaseDescriptor):
|
184
|
-
"""Validate that the `option_label` attribute is a string.
|
185
|
-
|
186
|
-
>>> class TestQuestion:
|
187
|
-
... option_label = OptionLabelDescriptor()
|
188
|
-
... def __init__(self, option_label: str):
|
189
|
-
... self.option_label = option_label
|
190
|
-
|
191
|
-
>>> _ = TestQuestion("{{Option}}")
|
192
|
-
|
193
|
-
"""
|
184
|
+
"""Validate that the `option_label` attribute is a string."""
|
194
185
|
|
195
186
|
def validate(self, value, instance):
|
196
187
|
"""Validate the value is a string."""
|
197
|
-
|
198
|
-
if "{{" in value and "}}" in value:
|
199
|
-
# they're trying to use a dynamic question name - let's let this play out
|
200
|
-
return None
|
201
|
-
|
202
|
-
key_values = [int(v) for v in value.keys()]
|
188
|
+
# key_values = [int(v) for v in value.keys()]
|
203
189
|
|
204
190
|
if value and (key_values := [float(v) for v in value.keys()]) != []:
|
205
191
|
if min(key_values) != min(instance.question_options):
|
@@ -96,7 +96,7 @@ class Question(metaclass=Meta):
|
|
96
96
|
|
97
97
|
>>> from edsl import Question
|
98
98
|
>>> Question.list_question_types()
|
99
|
-
['checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', '
|
99
|
+
['checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
|
100
100
|
"""
|
101
101
|
return [
|
102
102
|
q
|
edsl/results/CSSParameterizer.py
CHANGED
@@ -67,7 +67,7 @@ class CSSParameterizer:
|
|
67
67
|
missing_vars = self._validate_parameters(parameters)
|
68
68
|
|
69
69
|
if missing_vars:
|
70
|
-
|
70
|
+
print(f"Error: Missing required variables: {missing_vars}")
|
71
71
|
return None
|
72
72
|
|
73
73
|
# Format parameters with -- prefix if not present
|