edsl 0.1.39.dev2__py3-none-any.whl → 0.1.39.dev3__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 (334) hide show
  1. edsl/Base.py +332 -385
  2. edsl/BaseDiff.py +260 -260
  3. edsl/TemplateLoader.py +24 -24
  4. edsl/__init__.py +49 -57
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +867 -1079
  7. edsl/agents/AgentList.py +413 -551
  8. edsl/agents/Invigilator.py +233 -285
  9. edsl/agents/InvigilatorBase.py +270 -254
  10. edsl/agents/PromptConstructor.py +354 -252
  11. edsl/agents/__init__.py +3 -2
  12. edsl/agents/descriptors.py +99 -99
  13. edsl/agents/prompt_helpers.py +129 -129
  14. edsl/auto/AutoStudy.py +117 -117
  15. edsl/auto/StageBase.py +230 -230
  16. edsl/auto/StageGenerateSurvey.py +178 -178
  17. edsl/auto/StageLabelQuestions.py +125 -125
  18. edsl/auto/StagePersona.py +61 -61
  19. edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
  20. edsl/auto/StagePersonaDimensionValues.py +74 -74
  21. edsl/auto/StagePersonaDimensions.py +69 -69
  22. edsl/auto/StageQuestions.py +73 -73
  23. edsl/auto/SurveyCreatorPipeline.py +21 -21
  24. edsl/auto/utilities.py +224 -224
  25. edsl/base/Base.py +279 -279
  26. edsl/config.py +157 -177
  27. edsl/conversation/Conversation.py +290 -290
  28. edsl/conversation/car_buying.py +58 -59
  29. edsl/conversation/chips.py +95 -95
  30. edsl/conversation/mug_negotiation.py +81 -81
  31. edsl/conversation/next_speaker_utilities.py +93 -93
  32. edsl/coop/PriceFetcher.py +54 -54
  33. edsl/coop/__init__.py +2 -2
  34. edsl/coop/coop.py +1028 -1090
  35. edsl/coop/utils.py +131 -131
  36. edsl/data/Cache.py +555 -562
  37. edsl/data/CacheEntry.py +233 -230
  38. edsl/data/CacheHandler.py +149 -170
  39. edsl/data/RemoteCacheSync.py +78 -78
  40. edsl/data/SQLiteDict.py +292 -292
  41. edsl/data/__init__.py +4 -5
  42. edsl/data/orm.py +10 -10
  43. edsl/data_transfer_models.py +73 -74
  44. edsl/enums.py +175 -195
  45. edsl/exceptions/BaseException.py +21 -21
  46. edsl/exceptions/__init__.py +54 -54
  47. edsl/exceptions/agents.py +42 -54
  48. edsl/exceptions/cache.py +5 -5
  49. edsl/exceptions/configuration.py +16 -16
  50. edsl/exceptions/coop.py +10 -10
  51. edsl/exceptions/data.py +14 -14
  52. edsl/exceptions/general.py +34 -34
  53. edsl/exceptions/jobs.py +33 -33
  54. edsl/exceptions/language_models.py +63 -63
  55. edsl/exceptions/prompts.py +15 -15
  56. edsl/exceptions/questions.py +91 -109
  57. edsl/exceptions/results.py +29 -29
  58. edsl/exceptions/scenarios.py +22 -29
  59. edsl/exceptions/surveys.py +37 -37
  60. edsl/inference_services/AnthropicService.py +87 -84
  61. edsl/inference_services/AwsBedrock.py +120 -118
  62. edsl/inference_services/AzureAI.py +217 -215
  63. edsl/inference_services/DeepInfraService.py +18 -18
  64. edsl/inference_services/GoogleService.py +148 -139
  65. edsl/inference_services/GroqService.py +20 -20
  66. edsl/inference_services/InferenceServiceABC.py +147 -80
  67. edsl/inference_services/InferenceServicesCollection.py +97 -122
  68. edsl/inference_services/MistralAIService.py +123 -120
  69. edsl/inference_services/OllamaService.py +18 -18
  70. edsl/inference_services/OpenAIService.py +224 -221
  71. edsl/inference_services/PerplexityService.py +163 -160
  72. edsl/inference_services/TestService.py +89 -92
  73. edsl/inference_services/TogetherAIService.py +170 -170
  74. edsl/inference_services/models_available_cache.py +118 -118
  75. edsl/inference_services/rate_limits_cache.py +25 -25
  76. edsl/inference_services/registry.py +41 -41
  77. edsl/inference_services/write_available.py +10 -10
  78. edsl/jobs/Answers.py +56 -43
  79. edsl/jobs/Jobs.py +898 -757
  80. edsl/jobs/JobsChecks.py +147 -172
  81. edsl/jobs/JobsPrompts.py +268 -270
  82. edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
  83. edsl/jobs/__init__.py +1 -1
  84. edsl/jobs/buckets/BucketCollection.py +63 -104
  85. edsl/jobs/buckets/ModelBuckets.py +65 -65
  86. edsl/jobs/buckets/TokenBucket.py +251 -283
  87. edsl/jobs/interviews/Interview.py +661 -358
  88. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
  89. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
  90. edsl/jobs/interviews/InterviewStatistic.py +63 -63
  91. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
  92. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
  93. edsl/jobs/interviews/InterviewStatusLog.py +92 -92
  94. edsl/jobs/interviews/ReportErrors.py +66 -66
  95. edsl/jobs/interviews/interview_status_enum.py +9 -9
  96. edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
  97. edsl/jobs/runners/JobsRunnerStatus.py +330 -330
  98. edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
  99. edsl/jobs/tasks/TaskCreators.py +64 -64
  100. edsl/jobs/tasks/TaskHistory.py +450 -449
  101. edsl/jobs/tasks/TaskStatusLog.py +23 -23
  102. edsl/jobs/tasks/task_status_enum.py +163 -161
  103. edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
  104. edsl/jobs/tokens/TokenUsage.py +34 -34
  105. edsl/language_models/KeyLookup.py +30 -0
  106. edsl/language_models/LanguageModel.py +668 -571
  107. edsl/language_models/ModelList.py +155 -153
  108. edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
  109. edsl/language_models/__init__.py +3 -2
  110. edsl/language_models/fake_openai_call.py +15 -15
  111. edsl/language_models/fake_openai_service.py +61 -61
  112. edsl/language_models/registry.py +190 -180
  113. edsl/language_models/repair.py +156 -156
  114. edsl/language_models/unused/ReplicateBase.py +83 -0
  115. edsl/language_models/utilities.py +64 -65
  116. edsl/notebooks/Notebook.py +258 -263
  117. edsl/notebooks/__init__.py +1 -1
  118. edsl/prompts/Prompt.py +362 -352
  119. edsl/prompts/__init__.py +2 -2
  120. edsl/questions/AnswerValidatorMixin.py +289 -334
  121. edsl/questions/QuestionBase.py +664 -509
  122. edsl/questions/QuestionBaseGenMixin.py +161 -165
  123. edsl/questions/QuestionBasePromptsMixin.py +217 -221
  124. edsl/questions/QuestionBudget.py +227 -227
  125. edsl/questions/QuestionCheckBox.py +359 -359
  126. edsl/questions/QuestionExtract.py +182 -182
  127. edsl/questions/QuestionFreeText.py +114 -113
  128. edsl/questions/QuestionFunctional.py +166 -166
  129. edsl/questions/QuestionList.py +231 -229
  130. edsl/questions/QuestionMultipleChoice.py +286 -330
  131. edsl/questions/QuestionNumerical.py +153 -151
  132. edsl/questions/QuestionRank.py +324 -314
  133. edsl/questions/Quick.py +41 -41
  134. edsl/questions/RegisterQuestionsMeta.py +71 -71
  135. edsl/questions/ResponseValidatorABC.py +174 -200
  136. edsl/questions/SimpleAskMixin.py +73 -74
  137. edsl/questions/__init__.py +26 -27
  138. edsl/questions/compose_questions.py +98 -98
  139. edsl/questions/decorators.py +21 -21
  140. edsl/questions/derived/QuestionLikertFive.py +76 -76
  141. edsl/questions/derived/QuestionLinearScale.py +87 -90
  142. edsl/questions/derived/QuestionTopK.py +93 -93
  143. edsl/questions/derived/QuestionYesNo.py +82 -82
  144. edsl/questions/descriptors.py +413 -427
  145. edsl/questions/prompt_templates/question_budget.jinja +13 -13
  146. edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
  147. edsl/questions/prompt_templates/question_extract.jinja +11 -11
  148. edsl/questions/prompt_templates/question_free_text.jinja +3 -3
  149. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
  150. edsl/questions/prompt_templates/question_list.jinja +17 -17
  151. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
  152. edsl/questions/prompt_templates/question_numerical.jinja +36 -36
  153. edsl/questions/question_registry.py +177 -177
  154. edsl/questions/settings.py +12 -12
  155. edsl/questions/templates/budget/answering_instructions.jinja +7 -7
  156. edsl/questions/templates/budget/question_presentation.jinja +7 -7
  157. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
  158. edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
  159. edsl/questions/templates/extract/answering_instructions.jinja +7 -7
  160. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
  161. edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
  162. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
  163. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
  164. edsl/questions/templates/list/answering_instructions.jinja +3 -3
  165. edsl/questions/templates/list/question_presentation.jinja +5 -5
  166. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
  167. edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
  168. edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
  169. edsl/questions/templates/numerical/question_presentation.jinja +6 -6
  170. edsl/questions/templates/rank/answering_instructions.jinja +11 -11
  171. edsl/questions/templates/rank/question_presentation.jinja +15 -15
  172. edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
  173. edsl/questions/templates/top_k/question_presentation.jinja +22 -22
  174. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
  175. edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
  176. edsl/results/CSSParameterizer.py +108 -108
  177. edsl/results/Dataset.py +424 -587
  178. edsl/results/DatasetExportMixin.py +731 -653
  179. edsl/results/DatasetTree.py +275 -295
  180. edsl/results/Result.py +465 -451
  181. edsl/results/Results.py +1165 -1172
  182. edsl/results/ResultsDBMixin.py +238 -0
  183. edsl/results/ResultsExportMixin.py +43 -45
  184. edsl/results/ResultsFetchMixin.py +33 -33
  185. edsl/results/ResultsGGMixin.py +121 -121
  186. edsl/results/ResultsToolsMixin.py +98 -98
  187. edsl/results/Selector.py +135 -145
  188. edsl/results/TableDisplay.py +198 -125
  189. edsl/results/__init__.py +2 -2
  190. edsl/results/table_display.css +77 -77
  191. edsl/results/tree_explore.py +115 -115
  192. edsl/scenarios/FileStore.py +632 -511
  193. edsl/scenarios/Scenario.py +601 -498
  194. edsl/scenarios/ScenarioHtmlMixin.py +64 -65
  195. edsl/scenarios/ScenarioJoin.py +127 -131
  196. edsl/scenarios/ScenarioList.py +1287 -1430
  197. edsl/scenarios/ScenarioListExportMixin.py +52 -45
  198. edsl/scenarios/ScenarioListPdfMixin.py +261 -239
  199. edsl/scenarios/__init__.py +4 -3
  200. edsl/shared.py +1 -1
  201. edsl/study/ObjectEntry.py +173 -173
  202. edsl/study/ProofOfWork.py +113 -113
  203. edsl/study/SnapShot.py +80 -80
  204. edsl/study/Study.py +528 -521
  205. edsl/study/__init__.py +4 -4
  206. edsl/surveys/DAG.py +148 -148
  207. edsl/surveys/Memory.py +31 -31
  208. edsl/surveys/MemoryPlan.py +244 -244
  209. edsl/surveys/Rule.py +326 -327
  210. edsl/surveys/RuleCollection.py +387 -385
  211. edsl/surveys/Survey.py +1801 -1229
  212. edsl/surveys/SurveyCSS.py +261 -273
  213. edsl/surveys/SurveyExportMixin.py +259 -259
  214. edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
  215. edsl/surveys/SurveyQualtricsImport.py +284 -284
  216. edsl/surveys/__init__.py +3 -5
  217. edsl/surveys/base.py +53 -53
  218. edsl/surveys/descriptors.py +56 -60
  219. edsl/surveys/instructions/ChangeInstruction.py +49 -48
  220. edsl/surveys/instructions/Instruction.py +65 -56
  221. edsl/surveys/instructions/InstructionCollection.py +77 -82
  222. edsl/templates/error_reporting/base.html +23 -23
  223. edsl/templates/error_reporting/exceptions_by_model.html +34 -34
  224. edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
  225. edsl/templates/error_reporting/exceptions_by_type.html +16 -16
  226. edsl/templates/error_reporting/interview_details.html +115 -115
  227. edsl/templates/error_reporting/interviews.html +19 -19
  228. edsl/templates/error_reporting/overview.html +4 -4
  229. edsl/templates/error_reporting/performance_plot.html +1 -1
  230. edsl/templates/error_reporting/report.css +73 -73
  231. edsl/templates/error_reporting/report.html +117 -117
  232. edsl/templates/error_reporting/report.js +25 -25
  233. edsl/tools/__init__.py +1 -1
  234. edsl/tools/clusters.py +192 -192
  235. edsl/tools/embeddings.py +27 -27
  236. edsl/tools/embeddings_plotting.py +118 -118
  237. edsl/tools/plotting.py +112 -112
  238. edsl/tools/summarize.py +18 -18
  239. edsl/utilities/SystemInfo.py +28 -28
  240. edsl/utilities/__init__.py +22 -22
  241. edsl/utilities/ast_utilities.py +25 -25
  242. edsl/utilities/data/Registry.py +6 -6
  243. edsl/utilities/data/__init__.py +1 -1
  244. edsl/utilities/data/scooter_results.json +1 -1
  245. edsl/utilities/decorators.py +77 -77
  246. edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
  247. edsl/utilities/interface.py +627 -627
  248. edsl/utilities/naming_utilities.py +263 -263
  249. edsl/utilities/repair_functions.py +28 -28
  250. edsl/utilities/restricted_python.py +70 -70
  251. edsl/utilities/utilities.py +424 -436
  252. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
  253. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
  254. edsl-0.1.39.dev3.dist-info/RECORD +277 -0
  255. edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
  256. edsl/agents/QuestionOptionProcessor.py +0 -172
  257. edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
  258. edsl/coop/CoopFunctionsMixin.py +0 -15
  259. edsl/coop/ExpectedParrotKeyHandler.py +0 -125
  260. edsl/exceptions/inference_services.py +0 -5
  261. edsl/inference_services/AvailableModelCacheHandler.py +0 -184
  262. edsl/inference_services/AvailableModelFetcher.py +0 -209
  263. edsl/inference_services/ServiceAvailability.py +0 -135
  264. edsl/inference_services/data_structures.py +0 -62
  265. edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
  266. edsl/jobs/FetchInvigilator.py +0 -40
  267. edsl/jobs/InterviewTaskManager.py +0 -98
  268. edsl/jobs/InterviewsConstructor.py +0 -48
  269. edsl/jobs/JobsComponentConstructor.py +0 -189
  270. edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
  271. edsl/jobs/RequestTokenEstimator.py +0 -30
  272. edsl/jobs/buckets/TokenBucketAPI.py +0 -211
  273. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  274. edsl/jobs/decorators.py +0 -35
  275. edsl/jobs/jobs_status_enums.py +0 -9
  276. edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
  277. edsl/language_models/ComputeCost.py +0 -63
  278. edsl/language_models/PriceManager.py +0 -127
  279. edsl/language_models/RawResponseHandler.py +0 -106
  280. edsl/language_models/ServiceDataSources.py +0 -0
  281. edsl/language_models/key_management/KeyLookup.py +0 -63
  282. edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
  283. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  284. edsl/language_models/key_management/__init__.py +0 -0
  285. edsl/language_models/key_management/models.py +0 -131
  286. edsl/notebooks/NotebookToLaTeX.py +0 -142
  287. edsl/questions/ExceptionExplainer.py +0 -77
  288. edsl/questions/HTMLQuestion.py +0 -103
  289. edsl/questions/LoopProcessor.py +0 -149
  290. edsl/questions/QuestionMatrix.py +0 -265
  291. edsl/questions/ResponseValidatorFactory.py +0 -28
  292. edsl/questions/templates/matrix/__init__.py +0 -1
  293. edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
  294. edsl/questions/templates/matrix/question_presentation.jinja +0 -20
  295. edsl/results/MarkdownToDocx.py +0 -122
  296. edsl/results/MarkdownToPDF.py +0 -111
  297. edsl/results/TextEditor.py +0 -50
  298. edsl/results/smart_objects.py +0 -96
  299. edsl/results/table_data_class.py +0 -12
  300. edsl/results/table_renderers.py +0 -118
  301. edsl/scenarios/ConstructDownloadLink.py +0 -109
  302. edsl/scenarios/DirectoryScanner.py +0 -96
  303. edsl/scenarios/DocumentChunker.py +0 -102
  304. edsl/scenarios/DocxScenario.py +0 -16
  305. edsl/scenarios/PdfExtractor.py +0 -40
  306. edsl/scenarios/ScenarioSelector.py +0 -156
  307. edsl/scenarios/file_methods.py +0 -85
  308. edsl/scenarios/handlers/__init__.py +0 -13
  309. edsl/scenarios/handlers/csv.py +0 -38
  310. edsl/scenarios/handlers/docx.py +0 -76
  311. edsl/scenarios/handlers/html.py +0 -37
  312. edsl/scenarios/handlers/json.py +0 -111
  313. edsl/scenarios/handlers/latex.py +0 -5
  314. edsl/scenarios/handlers/md.py +0 -51
  315. edsl/scenarios/handlers/pdf.py +0 -68
  316. edsl/scenarios/handlers/png.py +0 -39
  317. edsl/scenarios/handlers/pptx.py +0 -105
  318. edsl/scenarios/handlers/py.py +0 -294
  319. edsl/scenarios/handlers/sql.py +0 -313
  320. edsl/scenarios/handlers/sqlite.py +0 -149
  321. edsl/scenarios/handlers/txt.py +0 -33
  322. edsl/surveys/ConstructDAG.py +0 -92
  323. edsl/surveys/EditSurvey.py +0 -221
  324. edsl/surveys/InstructionHandler.py +0 -100
  325. edsl/surveys/MemoryManagement.py +0 -72
  326. edsl/surveys/RuleManager.py +0 -172
  327. edsl/surveys/Simulator.py +0 -75
  328. edsl/surveys/SurveyToApp.py +0 -141
  329. edsl/utilities/PrettyList.py +0 -56
  330. edsl/utilities/is_notebook.py +0 -18
  331. edsl/utilities/is_valid_variable_name.py +0 -11
  332. edsl/utilities/remove_edsl_version.py +0 -24
  333. edsl-0.1.39.dev2.dist-info/RECORD +0 -352
  334. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.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,200 +1,174 @@
1
- import logging
2
- from abc import ABC, abstractmethod
3
- from typing import Optional, Any, List, TypedDict
4
-
5
- from pydantic import BaseModel, Field, field_validator, ValidationError
6
-
7
- from edsl.exceptions.questions import QuestionAnswerValidationError
8
- from edsl.questions.ExceptionExplainer import ExceptionExplainer
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,
76
- returning the instantiated model.
77
-
78
- >>> rv = ResponseValidatorABC.example("numerical")
79
- >>> rv._base_validate({"answer": 42})
80
- ConstrainedNumericResponse(answer=42, comment=None, generated_tokens=None)
81
- """
82
- try:
83
- return self.response_model(**data)
84
- except ValidationError as e:
85
- raise QuestionAnswerValidationError(
86
- message=str(e), pydantic_error=e, data=data, model=self.response_model
87
- )
88
-
89
- def post_validation_answer_convert(self, data):
90
- return data
91
-
92
- class EdslAnswerDict(TypedDict):
93
- answer: Any
94
- comment: Optional[str]
95
- generated_tokens: Optional[str]
96
-
97
- def validate(
98
- self,
99
- raw_edsl_answer_dict: RawEdslAnswerDict,
100
- fix=False,
101
- verbose=False,
102
- replacement_dict: dict = None,
103
- ) -> EdslAnswerDict:
104
- """This is the main validation function.
105
-
106
- >>> rv = ResponseValidatorABC.example("numerical")
107
- >>> rv.validate({"answer": 42})
108
- {'answer': 42, 'comment': None, 'generated_tokens': None}
109
- >>> rv.max_value
110
- 86.7
111
- >>> rv.validate({"answer": "120"})
112
- Traceback (most recent call last):
113
- ...
114
- edsl.exceptions.questions.QuestionAnswerValidationError:...
115
- >>> from edsl import QuestionNumerical
116
- >>> q = QuestionNumerical.example()
117
- >>> q.permissive = True
118
- >>> rv = q.response_validator
119
- >>> rv.validate({"answer": "120"})
120
- {'answer': 120, 'comment': None, 'generated_tokens': None}
121
- >>> rv.validate({"answer": "poo"})
122
- Traceback (most recent call last):
123
- ...
124
- edsl.exceptions.questions.QuestionAnswerValidationError:...
125
- """
126
- proposed_edsl_answer_dict = self._preprocess(raw_edsl_answer_dict)
127
- try:
128
- pydantic_edsl_answer: BaseModel = self._base_validate(
129
- proposed_edsl_answer_dict
130
- )
131
- edsl_answer_dict = self._extract_answer(pydantic_edsl_answer)
132
- return self._post_process(edsl_answer_dict)
133
- except QuestionAnswerValidationError as e:
134
- return self._handle_exception(e, raw_edsl_answer_dict)
135
-
136
- def human_explanation(self, e: QuestionAnswerValidationError):
137
- explanation = ExceptionExplainer(e, model_response=e.data).explain()
138
- return explanation
139
- # return e
140
-
141
- def _handle_exception(self, e: Exception, raw_edsl_answer_dict) -> EdslAnswerDict:
142
- if self.fixes_tried == 0:
143
- self.original_exception = e
144
-
145
- if self.fixes_tried == 0 and hasattr(self, "fix"):
146
- self.fixes_tried += 1
147
- fixed_data = self.fix(raw_edsl_answer_dict)
148
- try:
149
- return self.validate(fixed_data, fix=True) # early return if validates
150
- except Exception as e:
151
- pass # we don't log failed fixes
152
-
153
- # If the exception is already a QuestionAnswerValidationError, raise it
154
- if isinstance(self.original_exception, QuestionAnswerValidationError):
155
- raise self.original_exception
156
-
157
- # If nothing worked, raise the original exception
158
- raise QuestionAnswerValidationError(
159
- message=self.original_exception,
160
- pydantic_error=self.original_exception,
161
- data=raw_edsl_answer_dict,
162
- model=self.response_model,
163
- )
164
-
165
- def _check_constraints(self, pydantic_edsl_answer: BaseModel) -> dict:
166
- pass
167
-
168
- def _extract_answer(self, response: BaseModel) -> EdslAnswerDict:
169
- return response.model_dump()
170
-
171
- def _post_process(self, edsl_answer_dict: EdslAnswerDict) -> EdslAnswerDict:
172
- return edsl_answer_dict
173
-
174
- @classmethod
175
- def example(cls, question_type="numerical"):
176
- from edsl import Question
177
-
178
- q = Question.example(question_type)
179
- return q.response_validator
180
-
181
-
182
- def main():
183
- rv = ResponseValidatorABC.example()
184
- print(rv.validate({"answer": 42}))
185
-
186
-
187
- # Example usage
188
- if __name__ == "__main__":
189
- import doctest
190
-
191
- doctest.testmod(optionflags=doctest.ELLIPSIS)
192
-
193
- rv = ResponseValidatorABC.example()
194
- # print(rv.validate({"answer": 42}))
195
-
196
- rv = ResponseValidatorABC.example()
197
- try:
198
- rv.validate({"answer": "120"})
199
- except QuestionAnswerValidationError as e:
200
- print(rv.human_explanation(e))
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)