edsl 0.1.14__py3-none-any.whl → 0.1.40__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 (407) hide show
  1. edsl/Base.py +348 -38
  2. edsl/BaseDiff.py +260 -0
  3. edsl/TemplateLoader.py +24 -0
  4. edsl/__init__.py +46 -10
  5. edsl/__version__.py +1 -0
  6. edsl/agents/Agent.py +842 -144
  7. edsl/agents/AgentList.py +521 -25
  8. edsl/agents/Invigilator.py +250 -374
  9. edsl/agents/InvigilatorBase.py +257 -0
  10. edsl/agents/PromptConstructor.py +272 -0
  11. edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
  12. edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
  13. edsl/agents/descriptors.py +43 -13
  14. edsl/agents/prompt_helpers.py +129 -0
  15. edsl/agents/question_option_processor.py +172 -0
  16. edsl/auto/AutoStudy.py +130 -0
  17. edsl/auto/StageBase.py +243 -0
  18. edsl/auto/StageGenerateSurvey.py +178 -0
  19. edsl/auto/StageLabelQuestions.py +125 -0
  20. edsl/auto/StagePersona.py +61 -0
  21. edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
  22. edsl/auto/StagePersonaDimensionValues.py +74 -0
  23. edsl/auto/StagePersonaDimensions.py +69 -0
  24. edsl/auto/StageQuestions.py +74 -0
  25. edsl/auto/SurveyCreatorPipeline.py +21 -0
  26. edsl/auto/utilities.py +218 -0
  27. edsl/base/Base.py +279 -0
  28. edsl/config.py +121 -104
  29. edsl/conversation/Conversation.py +290 -0
  30. edsl/conversation/car_buying.py +59 -0
  31. edsl/conversation/chips.py +95 -0
  32. edsl/conversation/mug_negotiation.py +81 -0
  33. edsl/conversation/next_speaker_utilities.py +93 -0
  34. edsl/coop/CoopFunctionsMixin.py +15 -0
  35. edsl/coop/ExpectedParrotKeyHandler.py +125 -0
  36. edsl/coop/PriceFetcher.py +54 -0
  37. edsl/coop/__init__.py +1 -0
  38. edsl/coop/coop.py +1029 -134
  39. edsl/coop/utils.py +131 -0
  40. edsl/data/Cache.py +560 -89
  41. edsl/data/CacheEntry.py +230 -0
  42. edsl/data/CacheHandler.py +168 -0
  43. edsl/data/RemoteCacheSync.py +186 -0
  44. edsl/data/SQLiteDict.py +292 -0
  45. edsl/data/__init__.py +5 -3
  46. edsl/data/orm.py +6 -33
  47. edsl/data_transfer_models.py +74 -27
  48. edsl/enums.py +165 -8
  49. edsl/exceptions/BaseException.py +21 -0
  50. edsl/exceptions/__init__.py +52 -46
  51. edsl/exceptions/agents.py +33 -15
  52. edsl/exceptions/cache.py +5 -0
  53. edsl/exceptions/coop.py +8 -0
  54. edsl/exceptions/general.py +34 -0
  55. edsl/exceptions/inference_services.py +5 -0
  56. edsl/exceptions/jobs.py +15 -0
  57. edsl/exceptions/language_models.py +46 -1
  58. edsl/exceptions/questions.py +80 -5
  59. edsl/exceptions/results.py +16 -5
  60. edsl/exceptions/scenarios.py +29 -0
  61. edsl/exceptions/surveys.py +13 -10
  62. edsl/inference_services/AnthropicService.py +106 -0
  63. edsl/inference_services/AvailableModelCacheHandler.py +184 -0
  64. edsl/inference_services/AvailableModelFetcher.py +215 -0
  65. edsl/inference_services/AwsBedrock.py +118 -0
  66. edsl/inference_services/AzureAI.py +215 -0
  67. edsl/inference_services/DeepInfraService.py +18 -0
  68. edsl/inference_services/GoogleService.py +143 -0
  69. edsl/inference_services/GroqService.py +20 -0
  70. edsl/inference_services/InferenceServiceABC.py +80 -0
  71. edsl/inference_services/InferenceServicesCollection.py +138 -0
  72. edsl/inference_services/MistralAIService.py +120 -0
  73. edsl/inference_services/OllamaService.py +18 -0
  74. edsl/inference_services/OpenAIService.py +236 -0
  75. edsl/inference_services/PerplexityService.py +160 -0
  76. edsl/inference_services/ServiceAvailability.py +135 -0
  77. edsl/inference_services/TestService.py +90 -0
  78. edsl/inference_services/TogetherAIService.py +172 -0
  79. edsl/inference_services/data_structures.py +134 -0
  80. edsl/inference_services/models_available_cache.py +118 -0
  81. edsl/inference_services/rate_limits_cache.py +25 -0
  82. edsl/inference_services/registry.py +41 -0
  83. edsl/inference_services/write_available.py +10 -0
  84. edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
  85. edsl/jobs/Answers.py +21 -20
  86. edsl/jobs/FetchInvigilator.py +47 -0
  87. edsl/jobs/InterviewTaskManager.py +98 -0
  88. edsl/jobs/InterviewsConstructor.py +50 -0
  89. edsl/jobs/Jobs.py +684 -204
  90. edsl/jobs/JobsChecks.py +172 -0
  91. edsl/jobs/JobsComponentConstructor.py +189 -0
  92. edsl/jobs/JobsPrompts.py +270 -0
  93. edsl/jobs/JobsRemoteInferenceHandler.py +311 -0
  94. edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
  95. edsl/jobs/RequestTokenEstimator.py +30 -0
  96. edsl/jobs/async_interview_runner.py +138 -0
  97. edsl/jobs/buckets/BucketCollection.py +104 -0
  98. edsl/jobs/buckets/ModelBuckets.py +65 -0
  99. edsl/jobs/buckets/TokenBucket.py +283 -0
  100. edsl/jobs/buckets/TokenBucketAPI.py +211 -0
  101. edsl/jobs/buckets/TokenBucketClient.py +191 -0
  102. edsl/jobs/check_survey_scenario_compatibility.py +85 -0
  103. edsl/jobs/data_structures.py +120 -0
  104. edsl/jobs/decorators.py +35 -0
  105. edsl/jobs/interviews/Interview.py +392 -0
  106. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -0
  107. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -0
  108. edsl/jobs/interviews/InterviewStatistic.py +63 -0
  109. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -0
  110. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -0
  111. edsl/jobs/interviews/InterviewStatusLog.py +92 -0
  112. edsl/jobs/interviews/ReportErrors.py +66 -0
  113. edsl/jobs/interviews/interview_status_enum.py +9 -0
  114. edsl/jobs/jobs_status_enums.py +9 -0
  115. edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
  116. edsl/jobs/results_exceptions_handler.py +98 -0
  117. edsl/jobs/runners/JobsRunnerAsyncio.py +151 -110
  118. edsl/jobs/runners/JobsRunnerStatus.py +298 -0
  119. edsl/jobs/tasks/QuestionTaskCreator.py +244 -0
  120. edsl/jobs/tasks/TaskCreators.py +64 -0
  121. edsl/jobs/tasks/TaskHistory.py +470 -0
  122. edsl/jobs/tasks/TaskStatusLog.py +23 -0
  123. edsl/jobs/tasks/task_status_enum.py +161 -0
  124. edsl/jobs/tokens/InterviewTokenUsage.py +27 -0
  125. edsl/jobs/tokens/TokenUsage.py +34 -0
  126. edsl/language_models/ComputeCost.py +63 -0
  127. edsl/language_models/LanguageModel.py +507 -386
  128. edsl/language_models/ModelList.py +164 -0
  129. edsl/language_models/PriceManager.py +127 -0
  130. edsl/language_models/RawResponseHandler.py +106 -0
  131. edsl/language_models/RegisterLanguageModelsMeta.py +184 -0
  132. edsl/language_models/__init__.py +1 -8
  133. edsl/language_models/fake_openai_call.py +15 -0
  134. edsl/language_models/fake_openai_service.py +61 -0
  135. edsl/language_models/key_management/KeyLookup.py +63 -0
  136. edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
  137. edsl/language_models/key_management/KeyLookupCollection.py +38 -0
  138. edsl/language_models/key_management/__init__.py +0 -0
  139. edsl/language_models/key_management/models.py +131 -0
  140. edsl/language_models/model.py +256 -0
  141. edsl/language_models/repair.py +109 -41
  142. edsl/language_models/utilities.py +65 -0
  143. edsl/notebooks/Notebook.py +263 -0
  144. edsl/notebooks/NotebookToLaTeX.py +142 -0
  145. edsl/notebooks/__init__.py +1 -0
  146. edsl/prompts/Prompt.py +222 -93
  147. edsl/prompts/__init__.py +1 -1
  148. edsl/questions/ExceptionExplainer.py +77 -0
  149. edsl/questions/HTMLQuestion.py +103 -0
  150. edsl/questions/QuestionBase.py +518 -0
  151. edsl/questions/QuestionBasePromptsMixin.py +221 -0
  152. edsl/questions/QuestionBudget.py +164 -67
  153. edsl/questions/QuestionCheckBox.py +281 -62
  154. edsl/questions/QuestionDict.py +343 -0
  155. edsl/questions/QuestionExtract.py +136 -50
  156. edsl/questions/QuestionFreeText.py +79 -55
  157. edsl/questions/QuestionFunctional.py +138 -41
  158. edsl/questions/QuestionList.py +184 -57
  159. edsl/questions/QuestionMatrix.py +265 -0
  160. edsl/questions/QuestionMultipleChoice.py +293 -69
  161. edsl/questions/QuestionNumerical.py +109 -56
  162. edsl/questions/QuestionRank.py +244 -49
  163. edsl/questions/Quick.py +41 -0
  164. edsl/questions/SimpleAskMixin.py +74 -0
  165. edsl/questions/__init__.py +9 -6
  166. edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +153 -38
  167. edsl/questions/compose_questions.py +13 -7
  168. edsl/questions/data_structures.py +20 -0
  169. edsl/questions/decorators.py +21 -0
  170. edsl/questions/derived/QuestionLikertFive.py +28 -26
  171. edsl/questions/derived/QuestionLinearScale.py +41 -28
  172. edsl/questions/derived/QuestionTopK.py +34 -26
  173. edsl/questions/derived/QuestionYesNo.py +40 -27
  174. edsl/questions/descriptors.py +228 -74
  175. edsl/questions/loop_processor.py +149 -0
  176. edsl/questions/prompt_templates/question_budget.jinja +13 -0
  177. edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
  178. edsl/questions/prompt_templates/question_extract.jinja +11 -0
  179. edsl/questions/prompt_templates/question_free_text.jinja +3 -0
  180. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
  181. edsl/questions/prompt_templates/question_list.jinja +17 -0
  182. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
  183. edsl/questions/prompt_templates/question_numerical.jinja +37 -0
  184. edsl/questions/question_base_gen_mixin.py +168 -0
  185. edsl/questions/question_registry.py +130 -46
  186. edsl/questions/register_questions_meta.py +71 -0
  187. edsl/questions/response_validator_abc.py +188 -0
  188. edsl/questions/response_validator_factory.py +34 -0
  189. edsl/questions/settings.py +5 -2
  190. edsl/questions/templates/__init__.py +0 -0
  191. edsl/questions/templates/budget/__init__.py +0 -0
  192. edsl/questions/templates/budget/answering_instructions.jinja +7 -0
  193. edsl/questions/templates/budget/question_presentation.jinja +7 -0
  194. edsl/questions/templates/checkbox/__init__.py +0 -0
  195. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
  196. edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
  197. edsl/questions/templates/dict/__init__.py +0 -0
  198. edsl/questions/templates/dict/answering_instructions.jinja +21 -0
  199. edsl/questions/templates/dict/question_presentation.jinja +1 -0
  200. edsl/questions/templates/extract/__init__.py +0 -0
  201. edsl/questions/templates/extract/answering_instructions.jinja +7 -0
  202. edsl/questions/templates/extract/question_presentation.jinja +1 -0
  203. edsl/questions/templates/free_text/__init__.py +0 -0
  204. edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  205. edsl/questions/templates/free_text/question_presentation.jinja +1 -0
  206. edsl/questions/templates/likert_five/__init__.py +0 -0
  207. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
  208. edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
  209. edsl/questions/templates/linear_scale/__init__.py +0 -0
  210. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
  211. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
  212. edsl/questions/templates/list/__init__.py +0 -0
  213. edsl/questions/templates/list/answering_instructions.jinja +4 -0
  214. edsl/questions/templates/list/question_presentation.jinja +5 -0
  215. edsl/questions/templates/matrix/__init__.py +1 -0
  216. edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
  217. edsl/questions/templates/matrix/question_presentation.jinja +20 -0
  218. edsl/questions/templates/multiple_choice/__init__.py +0 -0
  219. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
  220. edsl/questions/templates/multiple_choice/html.jinja +0 -0
  221. edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
  222. edsl/questions/templates/numerical/__init__.py +0 -0
  223. edsl/questions/templates/numerical/answering_instructions.jinja +7 -0
  224. edsl/questions/templates/numerical/question_presentation.jinja +7 -0
  225. edsl/questions/templates/rank/__init__.py +0 -0
  226. edsl/questions/templates/rank/answering_instructions.jinja +11 -0
  227. edsl/questions/templates/rank/question_presentation.jinja +15 -0
  228. edsl/questions/templates/top_k/__init__.py +0 -0
  229. edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
  230. edsl/questions/templates/top_k/question_presentation.jinja +22 -0
  231. edsl/questions/templates/yes_no/__init__.py +0 -0
  232. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
  233. edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
  234. edsl/results/CSSParameterizer.py +108 -0
  235. edsl/results/Dataset.py +550 -19
  236. edsl/results/DatasetExportMixin.py +594 -0
  237. edsl/results/DatasetTree.py +295 -0
  238. edsl/results/MarkdownToDocx.py +122 -0
  239. edsl/results/MarkdownToPDF.py +111 -0
  240. edsl/results/Result.py +477 -173
  241. edsl/results/Results.py +987 -269
  242. edsl/results/ResultsExportMixin.py +28 -125
  243. edsl/results/ResultsGGMixin.py +83 -15
  244. edsl/results/TableDisplay.py +125 -0
  245. edsl/results/TextEditor.py +50 -0
  246. edsl/results/__init__.py +1 -1
  247. edsl/results/file_exports.py +252 -0
  248. edsl/results/results_fetch_mixin.py +33 -0
  249. edsl/results/results_selector.py +145 -0
  250. edsl/results/results_tools_mixin.py +98 -0
  251. edsl/results/smart_objects.py +96 -0
  252. edsl/results/table_data_class.py +12 -0
  253. edsl/results/table_display.css +78 -0
  254. edsl/results/table_renderers.py +118 -0
  255. edsl/results/tree_explore.py +115 -0
  256. edsl/scenarios/ConstructDownloadLink.py +109 -0
  257. edsl/scenarios/DocumentChunker.py +102 -0
  258. edsl/scenarios/DocxScenario.py +16 -0
  259. edsl/scenarios/FileStore.py +543 -0
  260. edsl/scenarios/PdfExtractor.py +40 -0
  261. edsl/scenarios/Scenario.py +431 -62
  262. edsl/scenarios/ScenarioHtmlMixin.py +65 -0
  263. edsl/scenarios/ScenarioList.py +1415 -45
  264. edsl/scenarios/ScenarioListExportMixin.py +45 -0
  265. edsl/scenarios/ScenarioListPdfMixin.py +239 -0
  266. edsl/scenarios/__init__.py +2 -0
  267. edsl/scenarios/directory_scanner.py +96 -0
  268. edsl/scenarios/file_methods.py +85 -0
  269. edsl/scenarios/handlers/__init__.py +13 -0
  270. edsl/scenarios/handlers/csv.py +49 -0
  271. edsl/scenarios/handlers/docx.py +76 -0
  272. edsl/scenarios/handlers/html.py +37 -0
  273. edsl/scenarios/handlers/json.py +111 -0
  274. edsl/scenarios/handlers/latex.py +5 -0
  275. edsl/scenarios/handlers/md.py +51 -0
  276. edsl/scenarios/handlers/pdf.py +68 -0
  277. edsl/scenarios/handlers/png.py +39 -0
  278. edsl/scenarios/handlers/pptx.py +105 -0
  279. edsl/scenarios/handlers/py.py +294 -0
  280. edsl/scenarios/handlers/sql.py +313 -0
  281. edsl/scenarios/handlers/sqlite.py +149 -0
  282. edsl/scenarios/handlers/txt.py +33 -0
  283. edsl/scenarios/scenario_join.py +131 -0
  284. edsl/scenarios/scenario_selector.py +156 -0
  285. edsl/shared.py +1 -0
  286. edsl/study/ObjectEntry.py +173 -0
  287. edsl/study/ProofOfWork.py +113 -0
  288. edsl/study/SnapShot.py +80 -0
  289. edsl/study/Study.py +521 -0
  290. edsl/study/__init__.py +4 -0
  291. edsl/surveys/ConstructDAG.py +92 -0
  292. edsl/surveys/DAG.py +92 -11
  293. edsl/surveys/EditSurvey.py +221 -0
  294. edsl/surveys/InstructionHandler.py +100 -0
  295. edsl/surveys/Memory.py +9 -4
  296. edsl/surveys/MemoryManagement.py +72 -0
  297. edsl/surveys/MemoryPlan.py +156 -35
  298. edsl/surveys/Rule.py +221 -74
  299. edsl/surveys/RuleCollection.py +241 -61
  300. edsl/surveys/RuleManager.py +172 -0
  301. edsl/surveys/Simulator.py +75 -0
  302. edsl/surveys/Survey.py +1079 -339
  303. edsl/surveys/SurveyCSS.py +273 -0
  304. edsl/surveys/SurveyExportMixin.py +235 -40
  305. edsl/surveys/SurveyFlowVisualization.py +181 -0
  306. edsl/surveys/SurveyQualtricsImport.py +284 -0
  307. edsl/surveys/SurveyToApp.py +141 -0
  308. edsl/surveys/__init__.py +4 -2
  309. edsl/surveys/base.py +19 -3
  310. edsl/surveys/descriptors.py +17 -6
  311. edsl/surveys/instructions/ChangeInstruction.py +48 -0
  312. edsl/surveys/instructions/Instruction.py +56 -0
  313. edsl/surveys/instructions/InstructionCollection.py +82 -0
  314. edsl/surveys/instructions/__init__.py +0 -0
  315. edsl/templates/error_reporting/base.html +24 -0
  316. edsl/templates/error_reporting/exceptions_by_model.html +35 -0
  317. edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
  318. edsl/templates/error_reporting/exceptions_by_type.html +17 -0
  319. edsl/templates/error_reporting/interview_details.html +116 -0
  320. edsl/templates/error_reporting/interviews.html +19 -0
  321. edsl/templates/error_reporting/overview.html +5 -0
  322. edsl/templates/error_reporting/performance_plot.html +2 -0
  323. edsl/templates/error_reporting/report.css +74 -0
  324. edsl/templates/error_reporting/report.html +118 -0
  325. edsl/templates/error_reporting/report.js +25 -0
  326. edsl/tools/__init__.py +1 -0
  327. edsl/tools/clusters.py +192 -0
  328. edsl/tools/embeddings.py +27 -0
  329. edsl/tools/embeddings_plotting.py +118 -0
  330. edsl/tools/plotting.py +112 -0
  331. edsl/tools/summarize.py +18 -0
  332. edsl/utilities/PrettyList.py +56 -0
  333. edsl/utilities/SystemInfo.py +5 -0
  334. edsl/utilities/__init__.py +21 -20
  335. edsl/utilities/ast_utilities.py +3 -0
  336. edsl/utilities/data/Registry.py +2 -0
  337. edsl/utilities/decorators.py +41 -0
  338. edsl/utilities/gcp_bucket/__init__.py +0 -0
  339. edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
  340. edsl/utilities/interface.py +310 -60
  341. edsl/utilities/is_notebook.py +18 -0
  342. edsl/utilities/is_valid_variable_name.py +11 -0
  343. edsl/utilities/naming_utilities.py +263 -0
  344. edsl/utilities/remove_edsl_version.py +24 -0
  345. edsl/utilities/repair_functions.py +28 -0
  346. edsl/utilities/restricted_python.py +70 -0
  347. edsl/utilities/utilities.py +203 -13
  348. edsl-0.1.40.dist-info/METADATA +111 -0
  349. edsl-0.1.40.dist-info/RECORD +362 -0
  350. {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/WHEEL +1 -1
  351. edsl/agents/AgentListExportMixin.py +0 -24
  352. edsl/coop/old.py +0 -31
  353. edsl/data/Database.py +0 -141
  354. edsl/data/crud.py +0 -121
  355. edsl/jobs/Interview.py +0 -417
  356. edsl/jobs/JobsRunner.py +0 -63
  357. edsl/jobs/JobsRunnerStatusMixin.py +0 -115
  358. edsl/jobs/base.py +0 -47
  359. edsl/jobs/buckets.py +0 -166
  360. edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
  361. edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
  362. edsl/jobs/task_management.py +0 -218
  363. edsl/jobs/token_tracking.py +0 -78
  364. edsl/language_models/DeepInfra.py +0 -69
  365. edsl/language_models/OpenAI.py +0 -98
  366. edsl/language_models/model_interfaces/GeminiPro.py +0 -66
  367. edsl/language_models/model_interfaces/LanguageModelOpenAIFour.py +0 -8
  368. edsl/language_models/model_interfaces/LanguageModelOpenAIThreeFiveTurbo.py +0 -8
  369. edsl/language_models/model_interfaces/LlamaTwo13B.py +0 -21
  370. edsl/language_models/model_interfaces/LlamaTwo70B.py +0 -21
  371. edsl/language_models/model_interfaces/Mixtral8x7B.py +0 -24
  372. edsl/language_models/registry.py +0 -81
  373. edsl/language_models/schemas.py +0 -15
  374. edsl/language_models/unused/ReplicateBase.py +0 -83
  375. edsl/prompts/QuestionInstructionsBase.py +0 -6
  376. edsl/prompts/library/agent_instructions.py +0 -29
  377. edsl/prompts/library/agent_persona.py +0 -17
  378. edsl/prompts/library/question_budget.py +0 -26
  379. edsl/prompts/library/question_checkbox.py +0 -32
  380. edsl/prompts/library/question_extract.py +0 -19
  381. edsl/prompts/library/question_freetext.py +0 -14
  382. edsl/prompts/library/question_linear_scale.py +0 -20
  383. edsl/prompts/library/question_list.py +0 -22
  384. edsl/prompts/library/question_multiple_choice.py +0 -44
  385. edsl/prompts/library/question_numerical.py +0 -31
  386. edsl/prompts/library/question_rank.py +0 -21
  387. edsl/prompts/prompt_config.py +0 -33
  388. edsl/prompts/registry.py +0 -185
  389. edsl/questions/Question.py +0 -240
  390. edsl/report/InputOutputDataTypes.py +0 -134
  391. edsl/report/RegressionMixin.py +0 -28
  392. edsl/report/ReportOutputs.py +0 -1228
  393. edsl/report/ResultsFetchMixin.py +0 -106
  394. edsl/report/ResultsOutputMixin.py +0 -14
  395. edsl/report/demo.ipynb +0 -645
  396. edsl/results/ResultsDBMixin.py +0 -184
  397. edsl/surveys/SurveyFlowVisualizationMixin.py +0 -92
  398. edsl/trackers/Tracker.py +0 -91
  399. edsl/trackers/TrackerAPI.py +0 -196
  400. edsl/trackers/TrackerTasks.py +0 -70
  401. edsl/utilities/pastebin.py +0 -141
  402. edsl-0.1.14.dist-info/METADATA +0 -69
  403. edsl-0.1.14.dist-info/RECORD +0 -141
  404. /edsl/{language_models/model_interfaces → inference_services}/__init__.py +0 -0
  405. /edsl/{report/__init__.py → jobs/runners/JobsRunnerStatusData.py} +0 -0
  406. /edsl/{trackers/__init__.py → language_models/ServiceDataSources.py} +0 -0
  407. {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -0,0 +1,343 @@
1
+ from __future__ import annotations
2
+ from typing import Union, Optional, Dict, List, Any, Type
3
+ from pydantic import BaseModel, Field, field_validator
4
+ from jinja2 import Environment, FileSystemLoader, TemplateNotFound
5
+ from pathlib import Path
6
+
7
+ from edsl.questions.QuestionBase import QuestionBase
8
+ from edsl.questions.descriptors import (
9
+ AnswerKeysDescriptor,
10
+ ValueTypesDescriptor,
11
+ ValueDescriptionsDescriptor,
12
+ QuestionTextDescriptor,
13
+ )
14
+ from edsl.questions.response_validator_abc import ResponseValidatorABC
15
+ from edsl.exceptions.questions import QuestionCreationValidationError
16
+ from edsl.questions.decorators import inject_exception
17
+
18
+
19
+ class DictResponseValidator(ResponseValidatorABC):
20
+ required_params = ["answer_keys", "permissive"]
21
+
22
+ valid_examples = [
23
+ (
24
+ {
25
+ "answer": {
26
+ "name": "Hot Chocolate",
27
+ "num_ingredients": 5,
28
+ "ingredients": ["milk", "cocoa", "sugar"]
29
+ }
30
+ },
31
+ {
32
+ "answer_keys": ["name", "num_ingredients", "ingredients"],
33
+ "value_types": ["str", "int", "list[str]"]
34
+ },
35
+ )
36
+ ]
37
+ invalid_examples = [
38
+ (
39
+ {"answer": {"name": 123}}, # Name should be a string
40
+ {"answer_keys": ["name"], "value_types": ["str"]},
41
+ "Key 'name' has value of type int, expected str",
42
+ ),
43
+ (
44
+ {"answer": {"ingredients": "milk"}}, # Should be a list
45
+ {"answer_keys": ["ingredients"], "value_types": ["list"]},
46
+ "Key 'ingredients' should be a list, got str",
47
+ )
48
+ ]
49
+
50
+
51
+ class QuestionDict(QuestionBase):
52
+ question_type = "dict"
53
+ question_text: str = QuestionTextDescriptor()
54
+ answer_keys: List[str] = AnswerKeysDescriptor()
55
+ value_types: Optional[List[str]] = ValueTypesDescriptor()
56
+ value_descriptions: Optional[List[str]] = ValueDescriptionsDescriptor()
57
+
58
+ _response_model = None
59
+ response_validator_class = DictResponseValidator
60
+
61
+ def _get_default_answer(self) -> Dict[str, Any]:
62
+ """Get default answer based on types."""
63
+ answer = {}
64
+ if not self.value_types:
65
+ return {
66
+ "title": "Sample Recipe",
67
+ "ingredients": ["ingredient1", "ingredient2"],
68
+ "num_ingredients": 2,
69
+ "instructions": "Sample instructions"
70
+ }
71
+
72
+ for key, type_str in zip(self.answer_keys, self.value_types):
73
+ if type_str.startswith(('list[', 'list')):
74
+ if '[' in type_str:
75
+ element_type = type_str[type_str.index('[') + 1:type_str.rindex(']')].lower()
76
+ if element_type == 'str':
77
+ answer[key] = ["sample_string"]
78
+ elif element_type == 'int':
79
+ answer[key] = [1]
80
+ elif element_type == 'float':
81
+ answer[key] = [1.0]
82
+ else:
83
+ answer[key] = []
84
+ else:
85
+ answer[key] = []
86
+ else:
87
+ if type_str == 'str':
88
+ answer[key] = "sample_string"
89
+ elif type_str == 'int':
90
+ answer[key] = 1
91
+ elif type_str == 'float':
92
+ answer[key] = 1.0
93
+ else:
94
+ answer[key] = None
95
+
96
+ return answer
97
+
98
+ def create_response_model(
99
+ self,
100
+ ) -> Type[BaseModel]:
101
+ """Create a response model for dict questions."""
102
+ default_answer = self._get_default_answer()
103
+
104
+ class DictResponse(BaseModel):
105
+ answer: Dict[str, Any] = Field(
106
+ default_factory=lambda: default_answer.copy()
107
+ )
108
+ comment: Optional[str] = None
109
+
110
+ @field_validator("answer")
111
+ def validate_answer(cls, v, values, **kwargs):
112
+ # Ensure all keys exist
113
+ missing_keys = set(self.answer_keys) - set(v.keys())
114
+ if missing_keys:
115
+ raise ValueError(f"Missing required keys: {missing_keys}")
116
+
117
+ # Validate value types if not permissive
118
+ if not self.permissive and self.value_types:
119
+ for key, type_str in zip(self.answer_keys, self.value_types):
120
+ if key not in v:
121
+ continue
122
+
123
+ value = v[key]
124
+ type_str = type_str.lower() # Normalize to lowercase
125
+
126
+ # Handle list types
127
+ if type_str.startswith(('list[', 'list')):
128
+ if not isinstance(value, list):
129
+ raise ValueError(f"Key '{key}' should be a list, got {type(value).__name__}")
130
+
131
+ # If it's a parameterized list, check element types
132
+ if '[' in type_str:
133
+ element_type = type_str[type_str.index('[') + 1:type_str.rindex(']')]
134
+ element_type = element_type.lower().strip()
135
+
136
+ for i, elem in enumerate(value):
137
+ expected_type = {
138
+ 'str': str,
139
+ 'int': int,
140
+ 'float': float,
141
+ 'list': list
142
+ }.get(element_type)
143
+
144
+ if expected_type and not isinstance(elem, expected_type):
145
+ raise ValueError(
146
+ f"List element at index {i} for key '{key}' "
147
+ f"has type {type(elem).__name__}, expected {element_type}"
148
+ )
149
+ else:
150
+ # Handle basic types
151
+ expected_type = {
152
+ 'str': str,
153
+ 'int': int,
154
+ 'float': float,
155
+ 'list': list,
156
+ }.get(type_str)
157
+
158
+ if expected_type and not isinstance(value, expected_type):
159
+ raise ValueError(
160
+ f"Key '{key}' has value of type {type(value).__name__}, expected {type_str}"
161
+ )
162
+ return v
163
+
164
+ model_config = {
165
+ "json_schema_extra": {
166
+ "examples": [{
167
+ "answer": default_answer,
168
+ "comment": None
169
+ }]
170
+ }
171
+ }
172
+
173
+ DictResponse.__name__ = "DictResponse"
174
+ return DictResponse
175
+
176
+ def __init__(
177
+ self,
178
+ question_name: str,
179
+ question_text: str,
180
+ answer_keys: List[str],
181
+ value_types: Optional[List[Union[str, type]]] = None,
182
+ value_descriptions: Optional[List[str]] = None,
183
+ include_comment: bool = True,
184
+ question_presentation: Optional[str] = None,
185
+ answering_instructions: Optional[str] = None,
186
+ permissive: bool = False,
187
+ ):
188
+ self.question_name = question_name
189
+ self.question_text = question_text
190
+ self.answer_keys = answer_keys
191
+ self.value_types = self._normalize_value_types(value_types)
192
+ self.value_descriptions = value_descriptions
193
+ self.include_comment = include_comment
194
+ self.question_presentation = question_presentation or self._render_template(
195
+ "question_presentation.jinja"
196
+ )
197
+ self.answering_instructions = answering_instructions or self._render_template(
198
+ "answering_instructions.jinja"
199
+ )
200
+ self.permissive = permissive
201
+
202
+ # Validation
203
+ if self.value_types and len(self.value_types) != len(self.answer_keys):
204
+ raise QuestionCreationValidationError(
205
+ "Length of value_types must match length of answer_keys."
206
+ )
207
+ if self.value_descriptions and len(self.value_descriptions) != len(self.answer_keys):
208
+ raise QuestionCreationValidationError(
209
+ "Length of value_descriptions must match length of answer_keys."
210
+ )
211
+
212
+ @staticmethod
213
+ def _normalize_value_types(value_types: Optional[List[Union[str, type]]]) -> Optional[List[str]]:
214
+ """Convert all value_types to string representations, including type hints."""
215
+ if not value_types:
216
+ return None
217
+
218
+ def normalize_type(t) -> str:
219
+ # Handle string representations of List
220
+ t_str = str(t)
221
+ if t_str == 'List':
222
+ return 'list'
223
+
224
+ # Handle string inputs
225
+ if isinstance(t, str):
226
+ t = t.lower()
227
+ # Handle list types
228
+ if t.startswith(('list[', 'list')):
229
+ if '[' in t:
230
+ # Normalize the inner type
231
+ inner_type = t[t.index('[') + 1:t.rindex(']')].strip().lower()
232
+ return f"list[{inner_type}]"
233
+ return "list"
234
+ return t
235
+
236
+ # Handle List the same as list
237
+ if t_str == "<class 'List'>":
238
+ return "list"
239
+
240
+ # If it's list type
241
+ if t is list:
242
+ return "list"
243
+
244
+ # If it's a basic type
245
+ if hasattr(t, "__name__"):
246
+ return t.__name__.lower()
247
+
248
+ # If it's a typing.List
249
+ if t_str.startswith(('list[', 'list')):
250
+ return t_str.replace('typing.', '').lower()
251
+
252
+ # Handle generic types
253
+ if hasattr(t, "__origin__"):
254
+ origin = t.__origin__.__name__.lower()
255
+ args = [
256
+ arg.__name__.lower() if hasattr(arg, "__name__") else str(arg).lower()
257
+ for arg in t.__args__
258
+ ]
259
+ return f"{origin}[{', '.join(args)}]"
260
+
261
+ raise QuestionCreationValidationError(
262
+ f"Invalid type in value_types: {t}. Must be a type or string."
263
+ )
264
+
265
+ normalized = []
266
+ for t in value_types:
267
+ try:
268
+ normalized.append(normalize_type(t))
269
+ except Exception as e:
270
+ raise QuestionCreationValidationError(f"Error normalizing type {t}: {str(e)}")
271
+
272
+ return normalized
273
+
274
+ def _render_template(self, template_name: str) -> str:
275
+ """Render a template using Jinja."""
276
+ try:
277
+ template_dir = Path(__file__).parent / "templates" / "dict"
278
+ env = Environment(loader=FileSystemLoader(template_dir))
279
+ template = env.get_template(template_name)
280
+ return template.render(
281
+ question_name=self.question_name,
282
+ question_text=self.question_text,
283
+ answer_keys=self.answer_keys,
284
+ value_types=self.value_types,
285
+ value_descriptions=self.value_descriptions,
286
+ include_comment=self.include_comment,
287
+ )
288
+ except TemplateNotFound:
289
+ return f"Template {template_name} not found in {template_dir}."
290
+
291
+ def to_dict(self, add_edsl_version: bool = True) -> dict:
292
+ """Serialize to JSON-compatible dictionary."""
293
+ return {
294
+ "question_type": self.question_type,
295
+ "question_name": self.question_name,
296
+ "question_text": self.question_text,
297
+ "answer_keys": self.answer_keys,
298
+ "value_types": self.value_types or [],
299
+ "value_descriptions": self.value_descriptions or [],
300
+ "include_comment": self.include_comment,
301
+ "permissive": self.permissive,
302
+ }
303
+
304
+ @classmethod
305
+ def from_dict(cls, data: dict) -> 'QuestionDict':
306
+ """Recreate from a dictionary."""
307
+ return cls(
308
+ question_name=data["question_name"],
309
+ question_text=data["question_text"],
310
+ answer_keys=data["answer_keys"],
311
+ value_types=data.get("value_types"),
312
+ value_descriptions=data.get("value_descriptions"),
313
+ include_comment=data.get("include_comment", True),
314
+ permissive=data.get("permissive", False),
315
+ )
316
+
317
+ @classmethod
318
+ @inject_exception
319
+ def example(cls) -> 'QuestionDict':
320
+ """Return an example question."""
321
+ return cls(
322
+ question_name="example",
323
+ question_text="Please provide a simple recipe for hot chocolate.",
324
+ answer_keys=["title", "ingredients", "num_ingredients", "instructions"],
325
+ value_types=["str", "list[str]", "int", "str"],
326
+ value_descriptions=[
327
+ "The title of the recipe.",
328
+ "A list of ingredients.",
329
+ "The number of ingredients.",
330
+ "The instructions for making the recipe."
331
+ ],
332
+ )
333
+
334
+ def _simulate_answer(self) -> dict:
335
+ """Simulate an answer for the question."""
336
+ return {
337
+ "answer": self._get_default_answer(),
338
+ "comment": None
339
+ }
340
+
341
+ if __name__ == "__main__":
342
+ q = QuestionDict.example()
343
+ print(q.to_dict())
@@ -1,71 +1,152 @@
1
1
  from __future__ import annotations
2
- import re
3
2
  import json
4
- from typing import Any
5
- from edsl.questions import Question
3
+ import re
4
+
5
+ from typing import Any, Optional, Dict
6
+ from edsl.questions.QuestionBase import QuestionBase
6
7
  from edsl.questions.descriptors import AnswerTemplateDescriptor
7
- from edsl.scenarios import Scenario
8
- from edsl.utilities import random_string
9
8
 
9
+ from edsl.questions.response_validator_abc import ResponseValidatorABC
10
+ from edsl.questions.data_structures import BaseResponse
11
+ from edsl.questions.decorators import inject_exception
12
+
13
+ from typing import Dict, Any
14
+ from pydantic import create_model, Field
15
+
16
+
17
+ def extract_json(text, expected_keys, verbose=False):
18
+ # Escape special regex characters in keys
19
+ escaped_keys = [re.escape(key) for key in expected_keys]
20
+
21
+ # Create a pattern that looks for all expected keys
22
+ pattern = r"\{[^}]*" + r"[^}]*".join(escaped_keys) + r"[^}]*\}"
23
+
24
+ json_match = re.search(pattern, text)
25
+
26
+ if json_match:
27
+ json_str = json_match.group(0)
28
+ try:
29
+ # Parse the extracted string as JSON
30
+ json_data = json.loads(json_str)
31
+
32
+ # Verify that all expected keys are present
33
+ if all(key in json_data for key in expected_keys):
34
+ return json_data
35
+ else:
36
+ if verbose:
37
+ print(
38
+ "Error: Not all expected keys were found in the extracted JSON."
39
+ )
40
+ return None
41
+ except json.JSONDecodeError:
42
+ if verbose:
43
+ print("Error: The extracted content is not valid JSON.")
44
+ return None
45
+ else:
46
+ if verbose:
47
+ print("Error: No JSON-like structure found with all expected keys.")
48
+ return None
49
+
50
+
51
+ def dict_to_pydantic_model(input_dict: Dict[str, Any]) -> Any:
52
+ field_definitions = {
53
+ key: (type(value), Field(default=value)) for key, value in input_dict.items()
54
+ }
55
+
56
+ DynamicModel = create_model("DynamicModel", **field_definitions)
57
+
58
+ class AnswerModel(BaseResponse):
59
+ answer: "DynamicModel"
60
+ generated_tokens: Optional[str] = None
61
+ comment: Optional[str] = None
10
62
 
11
- class QuestionExtract(Question):
12
- """
13
- This question asks the user to extract values from a string, and return them in a given template.
63
+ return AnswerModel
14
64
 
15
- Arguments:
16
- - `question_name` is the name of the question (string)
17
- - `question_text` is the text of the question (string)
18
- - `answer_template` is the template for the answer (dictionary mapping strings to strings)
19
65
 
20
- Optional arguments:
21
- - `instructions` are the instructions for the question (string). If not provided, the default instructions are used. To view them, run `QuestionExtract.default_instructions`
66
+ class ExtractResponseValidator(ResponseValidatorABC):
67
+ required_params = ["answer_template"]
68
+ valid_examples = [({"answer": "This is great"}, {})]
69
+ invalid_examples = [
70
+ (
71
+ {"answer": None},
72
+ {"answer_template": {"name": "John Doe", "profession": "Carpenter"}},
73
+ "Result cannot be empty",
74
+ ),
75
+ ]
22
76
 
23
- For an example, run `QuestionExtract.example()`
24
- """
77
+ def custom_validate(self, response) -> BaseResponse:
78
+ return response.dict()
79
+
80
+ def fix(self, response, verbose=False):
81
+ raw_tokens = response["generated_tokens"]
82
+ if verbose:
83
+ print(f"Invalid response of QuestionExtract was: {raw_tokens}")
84
+ extracted_json = extract_json(raw_tokens, self.answer_template.keys(), verbose)
85
+ if verbose:
86
+ print("Proposed solution is: ", extracted_json)
87
+ return {
88
+ "answer": extracted_json,
89
+ "comment": response.get("comment", None),
90
+ "generated_tokens": raw_tokens,
91
+ }
92
+
93
+
94
+ class QuestionExtract(QuestionBase):
95
+ """This question prompts the agent to extract information from a string and return it in a given template."""
25
96
 
26
97
  question_type = "extract"
27
98
  answer_template: dict[str, Any] = AnswerTemplateDescriptor()
99
+ _response_model = None
100
+ response_validator_class = ExtractResponseValidator
28
101
 
29
102
  def __init__(
30
103
  self,
31
104
  question_text: str,
32
105
  answer_template: dict[str, Any],
33
106
  question_name: str,
107
+ answering_instructions: str = None,
108
+ question_presentation: str = None,
34
109
  ):
110
+ """Initialize the question.
111
+
112
+ :param question_name: The name of the question.
113
+ :param question_text: The text of the question.
114
+ :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
+ """
35
118
  self.question_name = question_name
36
119
  self.question_text = question_text
37
120
  self.answer_template = answer_template
121
+ self.answering_instructions = answering_instructions
122
+ self.question_presentation = question_presentation
38
123
 
39
- ################
40
- # Answer methods
41
- ################
42
- def validate_answer(self, answer: Any) -> dict[str, Any]:
43
- """Validates the answer"""
44
- # raw_json = answer["answer"]
45
- # fixed_json_data = re.sub(r"\'", '"', raw_json)
46
- # answer["answer"] = json.loads(fixed_json_data)
47
- self.validate_answer_template_basic(answer)
48
- # self.validate_answer_key_value(answer, "answer", dict)
49
-
50
- self.validate_answer_extract(answer)
51
- return answer
52
-
53
- def translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
54
- """Returns the answer in a human-readable format"""
55
- return answer
56
-
57
- def simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
58
- """Simulates a valid answer for debugging purposes"""
59
- return {
60
- "answer": {key: random_string() for key in self.answer_template.keys()},
61
- "comment": random_string(),
62
- }
124
+ def create_response_model(self):
125
+ return dict_to_pydantic_model(self.answer_template)
126
+
127
+ @property
128
+ def question_html_content(self) -> str:
129
+ from jinja2 import Template
130
+
131
+ question_html_content = Template(
132
+ """
133
+ {% for field, placeholder in answer_template.items() %}
134
+ <div>
135
+ <label for="{{ field }}">{{ field }}</label>
136
+ <input type="text" id="{{ field }}" name="{{ question_name }}[{{ field }}]" placeholder="{{ placeholder }}">
137
+ </div>
138
+ {% endfor %}
139
+ """
140
+ ).render(
141
+ question_name=self.question_name,
142
+ answer_template=self.answer_template,
143
+ )
144
+ return question_html_content
63
145
 
64
- ################
65
- # Helpful methods
66
- ################
67
146
  @classmethod
147
+ @inject_exception
68
148
  def example(cls) -> QuestionExtract:
149
+ """Return an example question."""
69
150
  return cls(
70
151
  question_name="extract_name",
71
152
  question_text="My name is Moby Dick. I have a PhD in astrology, but I'm actually a truck driver",
@@ -73,22 +154,27 @@ class QuestionExtract(Question):
73
154
  )
74
155
 
75
156
 
76
- # main
77
157
  def main():
158
+ """Administer a question and validate the answer."""
78
159
  from edsl.questions.QuestionExtract import QuestionExtract
79
160
 
80
161
  q = QuestionExtract.example()
81
162
  q.question_text
82
163
  q.question_name
83
164
  q.answer_template
84
- q.validate_answer({"answer": {"name": "Moby", "profession": "truck driver"}})
85
- q.translate_answer_code_to_answer(
165
+ q._validate_answer({"answer": {"name": "Moby", "profession": "truck driver"}})
166
+ q._translate_answer_code_to_answer(
86
167
  {"answer": {"name": "Moby", "profession": "truck driver"}}
87
168
  )
88
- # simulate answer
89
- q.simulate_answer()
90
- q.simulate_answer(human_readable=False)
91
- q.validate_answer(q.simulate_answer(human_readable=False))
169
+ q._simulate_answer()
170
+ q._simulate_answer(human_readable=False)
171
+ q._validate_answer(q._simulate_answer(human_readable=False))
92
172
  # serialization (inherits from Question)
93
173
  q.to_dict()
94
174
  assert q.from_dict(q.to_dict()) == q
175
+
176
+
177
+ if __name__ == "__main__":
178
+ import doctest
179
+
180
+ doctest.testmod(optionflags=doctest.ELLIPSIS)