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.
Files changed (212) hide show
  1. edsl/Base.py +116 -197
  2. edsl/__init__.py +7 -15
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +147 -351
  5. edsl/agents/AgentList.py +73 -211
  6. edsl/agents/Invigilator.py +50 -101
  7. edsl/agents/InvigilatorBase.py +70 -62
  8. edsl/agents/PromptConstructor.py +225 -143
  9. edsl/agents/__init__.py +1 -0
  10. edsl/agents/prompt_helpers.py +3 -3
  11. edsl/auto/AutoStudy.py +5 -18
  12. edsl/auto/StageBase.py +40 -53
  13. edsl/auto/StageQuestions.py +1 -2
  14. edsl/auto/utilities.py +6 -0
  15. edsl/config.py +2 -22
  16. edsl/conversation/car_buying.py +1 -2
  17. edsl/coop/PriceFetcher.py +1 -1
  18. edsl/coop/coop.py +47 -125
  19. edsl/coop/utils.py +14 -14
  20. edsl/data/Cache.py +27 -45
  21. edsl/data/CacheEntry.py +15 -12
  22. edsl/data/CacheHandler.py +12 -31
  23. edsl/data/RemoteCacheSync.py +46 -154
  24. edsl/data/__init__.py +3 -4
  25. edsl/data_transfer_models.py +1 -2
  26. edsl/enums.py +0 -27
  27. edsl/exceptions/__init__.py +50 -50
  28. edsl/exceptions/agents.py +0 -12
  29. edsl/exceptions/questions.py +6 -24
  30. edsl/exceptions/scenarios.py +0 -7
  31. edsl/inference_services/AnthropicService.py +19 -38
  32. edsl/inference_services/AwsBedrock.py +2 -0
  33. edsl/inference_services/AzureAI.py +2 -0
  34. edsl/inference_services/GoogleService.py +12 -7
  35. edsl/inference_services/InferenceServiceABC.py +85 -18
  36. edsl/inference_services/InferenceServicesCollection.py +79 -120
  37. edsl/inference_services/MistralAIService.py +3 -0
  38. edsl/inference_services/OpenAIService.py +35 -47
  39. edsl/inference_services/PerplexityService.py +3 -0
  40. edsl/inference_services/TestService.py +10 -11
  41. edsl/inference_services/TogetherAIService.py +3 -5
  42. edsl/jobs/Answers.py +14 -1
  43. edsl/jobs/Jobs.py +431 -356
  44. edsl/jobs/JobsChecks.py +10 -35
  45. edsl/jobs/JobsPrompts.py +4 -6
  46. edsl/jobs/JobsRemoteInferenceHandler.py +133 -205
  47. edsl/jobs/buckets/BucketCollection.py +3 -44
  48. edsl/jobs/buckets/TokenBucket.py +21 -53
  49. edsl/jobs/interviews/Interview.py +408 -143
  50. edsl/jobs/runners/JobsRunnerAsyncio.py +403 -88
  51. edsl/jobs/runners/JobsRunnerStatus.py +165 -133
  52. edsl/jobs/tasks/QuestionTaskCreator.py +19 -21
  53. edsl/jobs/tasks/TaskHistory.py +18 -38
  54. edsl/jobs/tasks/task_status_enum.py +2 -0
  55. edsl/language_models/KeyLookup.py +30 -0
  56. edsl/language_models/LanguageModel.py +236 -194
  57. edsl/language_models/ModelList.py +19 -28
  58. edsl/language_models/__init__.py +2 -1
  59. edsl/language_models/registry.py +190 -0
  60. edsl/language_models/repair.py +2 -2
  61. edsl/language_models/unused/ReplicateBase.py +83 -0
  62. edsl/language_models/utilities.py +4 -5
  63. edsl/notebooks/Notebook.py +14 -19
  64. edsl/prompts/Prompt.py +39 -29
  65. edsl/questions/{answer_validator_mixin.py → AnswerValidatorMixin.py} +2 -47
  66. edsl/questions/QuestionBase.py +214 -68
  67. edsl/questions/{question_base_gen_mixin.py → QuestionBaseGenMixin.py} +50 -57
  68. edsl/questions/QuestionBasePromptsMixin.py +3 -7
  69. edsl/questions/QuestionBudget.py +1 -1
  70. edsl/questions/QuestionCheckBox.py +3 -3
  71. edsl/questions/QuestionExtract.py +7 -5
  72. edsl/questions/QuestionFreeText.py +3 -2
  73. edsl/questions/QuestionList.py +18 -10
  74. edsl/questions/QuestionMultipleChoice.py +23 -67
  75. edsl/questions/QuestionNumerical.py +4 -2
  76. edsl/questions/QuestionRank.py +17 -7
  77. edsl/questions/{response_validator_abc.py → ResponseValidatorABC.py} +26 -40
  78. edsl/questions/SimpleAskMixin.py +3 -4
  79. edsl/questions/__init__.py +1 -2
  80. edsl/questions/derived/QuestionLinearScale.py +3 -6
  81. edsl/questions/derived/QuestionTopK.py +1 -1
  82. edsl/questions/descriptors.py +3 -17
  83. edsl/questions/question_registry.py +1 -1
  84. edsl/results/CSSParameterizer.py +1 -1
  85. edsl/results/Dataset.py +7 -170
  86. edsl/results/DatasetExportMixin.py +305 -168
  87. edsl/results/DatasetTree.py +8 -28
  88. edsl/results/Result.py +206 -298
  89. edsl/results/Results.py +131 -149
  90. edsl/results/ResultsDBMixin.py +238 -0
  91. edsl/results/ResultsExportMixin.py +0 -2
  92. edsl/results/{results_selector.py → Selector.py} +13 -23
  93. edsl/results/TableDisplay.py +171 -98
  94. edsl/results/__init__.py +1 -1
  95. edsl/scenarios/FileStore.py +239 -150
  96. edsl/scenarios/Scenario.py +193 -90
  97. edsl/scenarios/ScenarioHtmlMixin.py +3 -4
  98. edsl/scenarios/{scenario_join.py → ScenarioJoin.py} +6 -10
  99. edsl/scenarios/ScenarioList.py +244 -415
  100. edsl/scenarios/ScenarioListExportMixin.py +7 -0
  101. edsl/scenarios/ScenarioListPdfMixin.py +37 -15
  102. edsl/scenarios/__init__.py +2 -1
  103. edsl/study/ObjectEntry.py +1 -1
  104. edsl/study/SnapShot.py +1 -1
  105. edsl/study/Study.py +12 -5
  106. edsl/surveys/Rule.py +4 -5
  107. edsl/surveys/RuleCollection.py +27 -25
  108. edsl/surveys/Survey.py +791 -270
  109. edsl/surveys/SurveyCSS.py +8 -20
  110. edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +9 -11
  111. edsl/surveys/__init__.py +2 -4
  112. edsl/surveys/descriptors.py +2 -6
  113. edsl/surveys/instructions/ChangeInstruction.py +2 -1
  114. edsl/surveys/instructions/Instruction.py +13 -4
  115. edsl/surveys/instructions/InstructionCollection.py +6 -11
  116. edsl/templates/error_reporting/interview_details.html +1 -1
  117. edsl/templates/error_reporting/report.html +1 -1
  118. edsl/tools/plotting.py +1 -1
  119. edsl/utilities/utilities.py +23 -35
  120. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/METADATA +10 -12
  121. edsl-0.1.39.dev1.dist-info/RECORD +277 -0
  122. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/WHEEL +1 -1
  123. edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
  124. edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
  125. edsl/agents/question_option_processor.py +0 -172
  126. edsl/coop/CoopFunctionsMixin.py +0 -15
  127. edsl/coop/ExpectedParrotKeyHandler.py +0 -125
  128. edsl/exceptions/inference_services.py +0 -5
  129. edsl/inference_services/AvailableModelCacheHandler.py +0 -184
  130. edsl/inference_services/AvailableModelFetcher.py +0 -215
  131. edsl/inference_services/ServiceAvailability.py +0 -135
  132. edsl/inference_services/data_structures.py +0 -134
  133. edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -223
  134. edsl/jobs/FetchInvigilator.py +0 -47
  135. edsl/jobs/InterviewTaskManager.py +0 -98
  136. edsl/jobs/InterviewsConstructor.py +0 -50
  137. edsl/jobs/JobsComponentConstructor.py +0 -189
  138. edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
  139. edsl/jobs/RequestTokenEstimator.py +0 -30
  140. edsl/jobs/async_interview_runner.py +0 -138
  141. edsl/jobs/buckets/TokenBucketAPI.py +0 -211
  142. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  143. edsl/jobs/check_survey_scenario_compatibility.py +0 -85
  144. edsl/jobs/data_structures.py +0 -120
  145. edsl/jobs/decorators.py +0 -35
  146. edsl/jobs/jobs_status_enums.py +0 -9
  147. edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
  148. edsl/jobs/results_exceptions_handler.py +0 -98
  149. edsl/language_models/ComputeCost.py +0 -63
  150. edsl/language_models/PriceManager.py +0 -127
  151. edsl/language_models/RawResponseHandler.py +0 -106
  152. edsl/language_models/ServiceDataSources.py +0 -0
  153. edsl/language_models/key_management/KeyLookup.py +0 -63
  154. edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
  155. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  156. edsl/language_models/key_management/__init__.py +0 -0
  157. edsl/language_models/key_management/models.py +0 -131
  158. edsl/language_models/model.py +0 -256
  159. edsl/notebooks/NotebookToLaTeX.py +0 -142
  160. edsl/questions/ExceptionExplainer.py +0 -77
  161. edsl/questions/HTMLQuestion.py +0 -103
  162. edsl/questions/QuestionMatrix.py +0 -265
  163. edsl/questions/data_structures.py +0 -20
  164. edsl/questions/loop_processor.py +0 -149
  165. edsl/questions/response_validator_factory.py +0 -34
  166. edsl/questions/templates/matrix/__init__.py +0 -1
  167. edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
  168. edsl/questions/templates/matrix/question_presentation.jinja +0 -20
  169. edsl/results/MarkdownToDocx.py +0 -122
  170. edsl/results/MarkdownToPDF.py +0 -111
  171. edsl/results/TextEditor.py +0 -50
  172. edsl/results/file_exports.py +0 -252
  173. edsl/results/smart_objects.py +0 -96
  174. edsl/results/table_data_class.py +0 -12
  175. edsl/results/table_renderers.py +0 -118
  176. edsl/scenarios/ConstructDownloadLink.py +0 -109
  177. edsl/scenarios/DocumentChunker.py +0 -102
  178. edsl/scenarios/DocxScenario.py +0 -16
  179. edsl/scenarios/PdfExtractor.py +0 -40
  180. edsl/scenarios/directory_scanner.py +0 -96
  181. edsl/scenarios/file_methods.py +0 -85
  182. edsl/scenarios/handlers/__init__.py +0 -13
  183. edsl/scenarios/handlers/csv.py +0 -49
  184. edsl/scenarios/handlers/docx.py +0 -76
  185. edsl/scenarios/handlers/html.py +0 -37
  186. edsl/scenarios/handlers/json.py +0 -111
  187. edsl/scenarios/handlers/latex.py +0 -5
  188. edsl/scenarios/handlers/md.py +0 -51
  189. edsl/scenarios/handlers/pdf.py +0 -68
  190. edsl/scenarios/handlers/png.py +0 -39
  191. edsl/scenarios/handlers/pptx.py +0 -105
  192. edsl/scenarios/handlers/py.py +0 -294
  193. edsl/scenarios/handlers/sql.py +0 -313
  194. edsl/scenarios/handlers/sqlite.py +0 -149
  195. edsl/scenarios/handlers/txt.py +0 -33
  196. edsl/scenarios/scenario_selector.py +0 -156
  197. edsl/surveys/ConstructDAG.py +0 -92
  198. edsl/surveys/EditSurvey.py +0 -221
  199. edsl/surveys/InstructionHandler.py +0 -100
  200. edsl/surveys/MemoryManagement.py +0 -72
  201. edsl/surveys/RuleManager.py +0 -172
  202. edsl/surveys/Simulator.py +0 -75
  203. edsl/surveys/SurveyToApp.py +0 -141
  204. edsl/utilities/PrettyList.py +0 -56
  205. edsl/utilities/is_notebook.py +0 -18
  206. edsl/utilities/is_valid_variable_name.py +0 -11
  207. edsl/utilities/remove_edsl_version.py +0 -24
  208. edsl-0.1.39.dist-info/RECORD +0 -358
  209. /edsl/questions/{register_questions_meta.py → RegisterQuestionsMeta.py} +0 -0
  210. /edsl/results/{results_fetch_mixin.py → ResultsFetchMixin.py} +0 -0
  211. /edsl/results/{results_tools_mixin.py → ResultsToolsMixin.py} +0 -0
  212. {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/LICENSE +0 -0
@@ -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.response_validator_abc import ResponseValidatorABC
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.response_validator_abc import ResponseValidatorABC
17
- from edsl.questions.data_structures import BaseResponse
16
+ from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
17
+ from edsl.questions.ResponseValidatorABC import BaseResponse
18
18
 
19
- from edsl.exceptions.questions import QuestionAnswerValidationError
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.response_validator_abc import ResponseValidatorABC
10
- from edsl.questions.data_structures import BaseResponse
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: "DynamicModel"
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.response_validator_abc import ResponseValidatorABC
8
+ from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
9
9
 
10
- from edsl.exceptions.questions import QuestionAnswerValidationError
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
 
@@ -1,18 +1,23 @@
1
1
  from __future__ import annotations
2
- import json
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
- def convert_string(s: str) -> Union[float, int, str, dict]:
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: bool) -> "ListResponse":
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.response_validator_abc import ResponseValidatorABC
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[list[str], list[list], list[float], list[int]] = (
124
- QuestionOptionsDescriptor()
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, replacements_dict: Optional[dict] = None
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
- try:
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.questions import QuestionAnswerValidationError
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.response_validator_abc import ResponseValidatorABC
13
+ from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
14
+ from edsl.exceptions.questions import QuestionAnswerValidationError
13
15
 
14
16
 
15
17
  def create_numeric_response(
@@ -1,14 +1,25 @@
1
1
  from __future__ import annotations
2
- from typing import Optional, Any, List, Annotated, Literal
3
-
4
- from pydantic import BaseModel, Field
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
- from edsl.questions.response_validator_abc import ResponseValidatorABC
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.Scenario import Scenario
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 pydantic import BaseModel, Field, field_validator, ValidationError
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
- from edsl.questions.data_structures import (
10
- RawEdslAnswerDict,
11
- EdslAnswerDict,
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 # how many times we've tried to fix the answer
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) # early return if validates
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
- message=self.original_exception,
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))
@@ -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.language_models.model import Model
68
+ from edsl import Model
70
69
 
71
70
  if model is None:
72
71
  model = Model()
@@ -1,6 +1,6 @@
1
1
  # Schemas
2
2
  from edsl.questions.settings import Settings
3
- from edsl.questions.register_questions_meta import RegisterQuestionsMeta
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
- if isinstance(option_labels, str):
44
- self.option_labels = option_labels
45
- else:
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.questions import QuestionCreationValidationError
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
 
@@ -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.questions import (
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
- if isinstance(value, str):
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', 'matrix', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
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
@@ -67,7 +67,7 @@ class CSSParameterizer:
67
67
  missing_vars = self._validate_parameters(parameters)
68
68
 
69
69
  if missing_vars:
70
- # print(f"Error: Missing required variables: {missing_vars}")
70
+ print(f"Error: Missing required variables: {missing_vars}")
71
71
  return None
72
72
 
73
73
  # Format parameters with -- prefix if not present