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
@@ -1,98 +1,151 @@
1
1
  from __future__ import annotations
2
- import textwrap
2
+
3
3
  from random import uniform
4
- from typing import Any, Optional, Union
5
- from edsl.exceptions import QuestionAnswerValidationError
6
- from edsl.questions import Question
4
+ from typing import Any, Optional, Union, Literal
5
+
6
+ from pydantic import BaseModel, Field, field_validator
7
+
8
+ from edsl.exceptions.questions import QuestionAnswerValidationError
9
+ from edsl.questions.QuestionBase import QuestionBase
7
10
  from edsl.questions.descriptors import NumericalOrNoneDescriptor
8
- from edsl.scenarios import Scenario
9
- from edsl.utilities import random_string
11
+ from edsl.questions.decorators import inject_exception
12
+ from edsl.questions.response_validator_abc import ResponseValidatorABC
10
13
 
11
14
 
12
- class QuestionNumerical(Question):
13
- """
14
- This question asks the user to answer with a numerical value.
15
+ def create_numeric_response(
16
+ min_value: Optional[float] = None,
17
+ max_value: Optional[float] = None,
18
+ permissive=False,
19
+ ):
20
+ field_kwargs = {}
21
+ if not permissive:
22
+ field_kwargs = {}
23
+ if min_value is not None:
24
+ field_kwargs["ge"] = min_value
25
+ if max_value is not None:
26
+ field_kwargs["le"] = max_value
27
+
28
+ class ConstrainedNumericResponse(BaseModel):
29
+ answer: Union[int, float] = Field(**field_kwargs)
30
+ comment: Optional[str] = Field(None)
31
+ generated_tokens: Optional[Any] = Field(None)
32
+
33
+ return ConstrainedNumericResponse
34
+
35
+
36
+ class NumericalResponseValidator(ResponseValidatorABC):
37
+ required_params = ["min_value", "max_value", "permissive"]
38
+
39
+ valid_examples = [
40
+ ({"answer": 1}, {"min_value": 0, "max_value": 10}),
41
+ ({"answer": 1}, {"min_value": None, "max_value": None}),
42
+ ]
43
+
44
+ invalid_examples = [
45
+ ({"answer": 10}, {"min_value": 0, "max_value": 5}, "Answer is out of range"),
46
+ ({"answer": "ten"}, {"min_value": 0, "max_value": 5}, "Answer is not a number"),
47
+ ({}, {"min_value": 0, "max_value": 5}, "Answer key is missing"),
48
+ ]
15
49
 
16
- Arguments:
17
- - `question_name` is the name of the question (string)
18
- - `question_text` is the text of the question (string)
50
+ def fix(self, response, verbose=False):
51
+ response_text = str(response).lower()
52
+ import re
19
53
 
20
- Optional arguments:
21
- - `min_value` is the minimum value of the answer (float)
22
- - `max_value` is the maximum value of the answer (float)
23
- - `instructions` are the instructions for the question (string). If not provided, the default instructions are used. To view them, run `QuestionNumerical.default_instructions`
54
+ if verbose:
55
+ print(f"Ivalid generated tokens was was: {response_text}")
56
+ pattern = r"\b\d+(?:\.\d+)?\b"
57
+ match = re.search(pattern, response_text.replace(",", ""))
58
+ solution = match.group(0) if match else response.get("answer")
59
+ if verbose:
60
+ print("Proposed solution is: ", solution)
61
+ if "comment" in response:
62
+ return {"answer": solution, "comment": response["comment"]}
63
+ else:
64
+ return {"answer": solution}
24
65
 
66
+ def _check_constraints(self, pydantic_edsl_answer: BaseModel):
67
+ pass
68
+
69
+
70
+ class QuestionNumerical(QuestionBase):
71
+ """This question prompts the agent to answer with a numerical value.
72
+
73
+ >>> QuestionNumerical.self_check()
25
74
 
26
- For an example, run `QuestionNumerical.example()`
27
75
  """
28
76
 
29
77
  question_type = "numerical"
30
78
  min_value: Optional[float] = NumericalOrNoneDescriptor()
31
79
  max_value: Optional[float] = NumericalOrNoneDescriptor()
32
80
 
81
+ _response_model = None
82
+ response_validator_class = NumericalResponseValidator
83
+
33
84
  def __init__(
34
85
  self,
35
86
  question_name: str,
36
87
  question_text: str,
37
88
  min_value: Optional[Union[int, float]] = None,
38
89
  max_value: Optional[Union[int, float]] = None,
90
+ include_comment: bool = True,
91
+ question_presentation: Optional[str] = None,
92
+ answering_instructions: Optional[str] = None,
93
+ permissive: bool = False,
39
94
  ):
95
+ """Initialize the question.
96
+
97
+ :param question_name: The name of the question.
98
+ :param question_text: The text of the question.
99
+ :param min_value: The minimum value of the answer.
100
+ :param max_value: The maximum value of the answer.
101
+ """
40
102
  self.question_name = question_name
41
103
  self.question_text = question_text
42
104
  self.min_value = min_value
43
105
  self.max_value = max_value
44
106
 
107
+ self.include_comment = include_comment
108
+ self.question_presentation = question_presentation
109
+ self.answering_instructions = answering_instructions
110
+ self.permissive = permissive
111
+
112
+ def create_response_model(self):
113
+ return create_numeric_response(self.min_value, self.max_value, self.permissive)
114
+
45
115
  ################
46
116
  # Answer methods
47
117
  ################
48
- def validate_answer(
49
- self, answer: dict[str, Any]
50
- ) -> dict[str, Union[str, float, int]]:
51
- """Validates the answer"""
52
- self.validate_answer_template_basic(answer)
53
- self.validate_answer_key_value_numeric(answer, "answer")
54
- self.validate_answer_numerical(answer)
55
- return answer
56
-
57
- def translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
58
- """There is no answer code."""
59
- return answer
60
-
61
- def simulate_answer(self, human_readable: bool = True):
62
- """Simulates a valid answer for debugging purposes"""
63
- return {
64
- "answer": uniform(self.min_value, self.max_value),
65
- "comment": random_string(),
66
- }
118
+
119
+ @property
120
+ def question_html_content(self) -> str:
121
+ from jinja2 import Template
122
+
123
+ question_html_content = Template(
124
+ """
125
+ <div>
126
+ <input type="number" id="{{ question_name }}" name="{{ question_name }}">
127
+ </div>
128
+ """
129
+ ).render(question_name=self.question_name)
130
+ return question_html_content
67
131
 
68
132
  ################
69
133
  # Helpful methods
70
134
  ################
71
135
  @classmethod
72
- def example(cls) -> QuestionNumerical:
136
+ @inject_exception
137
+ def example(cls, include_comment=False) -> QuestionNumerical:
138
+ """Return an example question."""
73
139
  return cls(
74
140
  question_name="age",
75
- question_text="How old are you in years?",
141
+ question_text="You are a 45 year old man. How old are you in years?",
76
142
  min_value=0,
77
143
  max_value=86.7,
144
+ include_comment=include_comment,
78
145
  )
79
146
 
80
147
 
81
- def main():
82
- from edsl.questions.QuestionNumerical import QuestionNumerical
83
-
84
- q = QuestionNumerical.example()
85
- q.question_text
86
- q.min_value
87
- q.max_value
88
- # validate an answer
89
- q.validate_answer({"answer": 1, "comment": "I like custard"})
90
- # translate answer code
91
- q.translate_answer_code_to_answer(1)
92
- # simulate answer
93
- q.simulate_answer()
94
- q.simulate_answer(human_readable=False)
95
- q.validate_answer(q.simulate_answer(human_readable=False))
96
- # serialization (inherits from Question)
97
- q.to_dict()
98
- assert q.from_dict(q.to_dict()) == q
148
+ if __name__ == "__main__":
149
+ import doctest
150
+
151
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -1,119 +1,314 @@
1
1
  from __future__ import annotations
2
- import random
3
- import textwrap
4
- from jinja2 import Template
5
- from typing import Any, Optional, Union
6
- from edsl.questions import Question
7
- from edsl.exceptions import QuestionAnswerValidationError
8
- from edsl.scenarios import Scenario
9
- from edsl.utilities.utilities import random_string
2
+ from typing import Optional, Any, List, Annotated, Literal
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from edsl.questions.QuestionBase import QuestionBase
10
7
  from edsl.questions.descriptors import (
11
8
  QuestionOptionsDescriptor,
12
9
  NumSelectionsDescriptor,
13
10
  )
11
+ from edsl.questions.response_validator_abc import ResponseValidatorABC
14
12
 
15
13
 
16
- class QuestionRank(Question):
14
+ def create_response_model(
15
+ choices: list,
16
+ num_selections: Optional[int] = None,
17
+ permissive: bool = False,
18
+ ):
19
+ """
20
+ :param choices: A list of allowed values for the answer field.
21
+ :param include_comment: Whether to include a comment field in the model.
22
+ :return: A new Pydantic model class.
17
23
  """
18
- This question asks the user to rank options from a list.
24
+ # Convert the choices list to a tuple for use with Literal
25
+ choice_tuple = tuple(choices)
19
26
 
20
- Arguments:
21
- - `question_name` is the name of the question (string)
22
- - `question_options` are the options the user should select from (list of strings)
23
- - `question_text` is the text of the question (string)
27
+ field_params = {}
28
+ if num_selections is not None and not permissive:
29
+ field_params["min_items"] = num_selections
30
+ field_params["max_items"] = num_selections
24
31
 
25
- Optional arguments:
26
- - `num_selections` is the number of options that must be selected (positive integer)
27
- - `instructions` are the instructions for the question (string). If not provided, the default instructions are used. To view them, run `QuestionRank.default_instructions`
28
- - `short_names_dict` maps question_options to short names (dictionary mapping strings to strings)
32
+ class RankResponse(BaseModel):
33
+ answer: Annotated[
34
+ List[Literal[choice_tuple]],
35
+ Field(..., **field_params),
36
+ ] = Field(..., description="List of selected choices")
37
+ comment: Optional[str] = Field(None, description="Optional comment field")
38
+ generated_tokens: Optional[Any] = Field(None)
29
39
 
30
- For an example, run `QuestionRank.example()`
31
- """
40
+ class Config:
41
+ @staticmethod
42
+ def json_schema_extra(schema: dict, model: BaseModel) -> None:
43
+ # Add the list of choices to the schema for better documentation
44
+ for prop in schema.get("properties", {}).values():
45
+ if prop.get("title") == "answer":
46
+ prop["items"] = {"enum": choices}
47
+
48
+ return RankResponse
49
+
50
+
51
+ class RankResponseValidator(ResponseValidatorABC):
52
+ required_params = ["num_selections", "permissive", "use_code", "question_options"]
53
+ valid_examples = []
54
+ invalid_examples = []
55
+
56
+ def fix(self, response, verbose=False):
57
+ if verbose:
58
+ print("Invalid response of QuestionRank was: ", False)
59
+ response_text = response.get("generated_tokens")
60
+ if response_text is None or response_text == "": # nothing to be done
61
+ return response
62
+ # Maybe it's a comma separated list?
63
+ response_text = str(response.get("answer"))
64
+ proposed_list = (
65
+ response_text.replace("[", "").replace("]", "").replace("'", "").split(",")
66
+ )
67
+ proposed_list = [item.strip() for item in proposed_list]
68
+
69
+ if verbose:
70
+ print("Using code? ", self.use_code)
71
+ if self.use_code:
72
+ try:
73
+ proposed_list = [int(i) for i in proposed_list]
74
+ except ValueError:
75
+ # print("Could not convert to int")
76
+ pass
77
+
78
+ if verbose:
79
+ print("Proposed solution is: ", proposed_list)
80
+
81
+ # print(f"Ivalid generated tokens was was: {response_text}")
82
+ if "comment" in response:
83
+ proposed_data = {
84
+ "answer": proposed_list,
85
+ "comment": response["comment"],
86
+ "generated_tokens": response.get("generated_tokens", None),
87
+ }
88
+ else:
89
+ proposed_data = {
90
+ "answer": proposed_list,
91
+ "generated_tokens": response.get("generated_tokens", None),
92
+ }
93
+
94
+ try:
95
+ self.response_model(**proposed_data)
96
+ return proposed_data
97
+ except Exception as e:
98
+ if verbose:
99
+ print(f"Proposed solution {proposed_data} is invalid. Error: {e}")
100
+ # return response
101
+ if verbose:
102
+ print("Now seeing if responses show up in the answer")
103
+ matches = []
104
+ for index, option in enumerate(self.question_options):
105
+ if self.use_code:
106
+ if str(index) in response_text:
107
+ if index not in matches:
108
+ matches.append(index)
109
+ else:
110
+ if option in response_text:
111
+ if option not in matches:
112
+ matches.append(option)
113
+ proposed_data = {
114
+ "answer": matches,
115
+ "comment": response.get("comment", None),
116
+ "generated_tokens": response.get("generated_tokens", None),
117
+ }
118
+ try:
119
+ self.response_model(**proposed_data)
120
+ return proposed_data
121
+ except Exception as e:
122
+ if verbose:
123
+ print(f"Proposed solution {proposed_data} is invalid. Error: {e}")
124
+ return response
125
+
126
+
127
+ class QuestionRank(QuestionBase):
128
+ """This question prompts the agent to rank options from a list."""
32
129
 
33
130
  question_type = "rank"
34
131
  question_options: list[str] = QuestionOptionsDescriptor()
35
132
  num_selections = NumSelectionsDescriptor()
36
133
 
134
+ _response_model = None
135
+ response_validator_class = RankResponseValidator
136
+
37
137
  def __init__(
38
138
  self,
39
139
  question_name: str,
40
140
  question_text: str,
41
141
  question_options: list[str],
42
142
  num_selections: Optional[int] = None,
43
- short_names_dict: Optional[dict[str, str]] = None,
143
+ question_presentation: Optional[str] = None,
144
+ answering_instructions: Optional[str] = None,
145
+ permissive: bool = False,
146
+ use_code: bool = True,
147
+ include_comment: bool = True,
44
148
  ):
149
+ """Initialize the question.
150
+
151
+ :param question_name: The name of the question.
152
+ :param question_text: The text of the question.
153
+ :param question_options: The options the respondent should select from.
154
+ :param min_selections: The minimum number of options that must be selected.
155
+ :param max_selections: The maximum number of options that must be selected.
156
+ """
45
157
  self.question_name = question_name
46
158
  self.question_text = question_text
47
159
  self.question_options = question_options
48
- self.short_names_dict = short_names_dict or dict()
49
160
  self.num_selections = num_selections or len(question_options)
161
+ self.question_presentation = question_presentation
162
+ self.answering_instructions = answering_instructions
163
+ self.permissive = permissive
164
+ self.use_code = use_code
165
+ self.include_comment = include_comment
166
+
167
+ def create_response_model(self):
168
+ choices = (
169
+ self.question_options
170
+ if not self.use_code
171
+ else range(len(self.question_options))
172
+ )
173
+ return create_response_model(
174
+ choices=choices,
175
+ num_selections=self.num_selections,
176
+ permissive=self.permissive,
177
+ )
50
178
 
51
179
  ################
52
180
  # Answer methods
53
181
  ################
54
- def validate_answer(self, answer: Any) -> dict[str, list[int]]:
55
- self.validate_answer_template_basic(answer)
56
- self.validate_answer_key_value(answer, "answer", list)
57
- self.validate_answer_rank(answer)
58
- return answer
182
+ # def _validate_answer(self, answer: Any) -> dict[str, list[int]]:
183
+ # """Validate the answer."""
184
+ # self._validate_answer_template_basic(answer)
185
+ # self._validate_answer_key_value(answer, "answer", list)
186
+ # self._validate_answer_rank(answer)
187
+ # return answer
59
188
 
60
- def translate_answer_code_to_answer(
189
+ def _translate_answer_code_to_answer(
61
190
  self, answer_codes, scenario: Scenario = None
62
191
  ) -> list[str]:
63
- """Translates the answer code to the actual answer."""
192
+ """Translate the answer code to the actual answer."""
193
+ from edsl.scenarios.Scenario import Scenario
194
+ from jinja2 import Template
195
+
64
196
  scenario = scenario or Scenario()
65
197
  translated_options = [
66
198
  Template(option).render(scenario) for option in self.question_options
67
199
  ]
68
200
  translated_codes = []
69
201
  for answer_code in answer_codes:
70
- translated_codes.append(translated_options[int(answer_code)])
202
+ if self._use_code:
203
+ translated_codes.append(translated_options[int(answer_code)])
204
+ else:
205
+ translated_codes.append(answer_code)
71
206
  return translated_codes
72
207
 
73
- def simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
74
- """Simulates a valid answer for debugging purposes"""
75
- if human_readable:
76
- selected = random.sample(self.question_options, self.num_selections)
77
- else:
78
- selected = random.sample(
79
- range(len(self.question_options)), self.num_selections
80
- )
81
- answer = {
82
- "answer": selected,
83
- "comment": random_string(),
208
+ # def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
209
+ # """Simulate a valid answer for debugging purposes."""
210
+ # from edsl.utilities.utilities import random_string
211
+
212
+ # if human_readable:
213
+ # selected = random.sample(self.question_options, self.num_selections)
214
+ # else:
215
+ # selected = random.sample(
216
+ # range(len(self.question_options)), self.num_selections
217
+ # )
218
+ # answer = {
219
+ # "answer": selected,
220
+ # "comment": random_string(),
221
+ # }
222
+ # return answer
223
+
224
+ @property
225
+ def question_html_content(self) -> str:
226
+ from jinja2 import Template
227
+
228
+ question_html_content = Template(
229
+ """
230
+ <form id="rankForm">
231
+ <p>{{ question_text }}</p>
232
+ {% for option in question_options %}
233
+ <div>
234
+ <label for="{{ option }}">{{ option }}</label>
235
+ <input type="number" id="{{ option }}" name="{{ question_name }}[{{ option }}]" value="0" min="1" max="{{ question_options|length }}" oninput="updateRankings()">
236
+ </div>
237
+ {% endfor %}
238
+ </form>
239
+ <script>
240
+ function updateRankings() {
241
+ let options = {{ question_options|length }};
242
+ let values = [];
243
+ let isValid = true;
244
+
245
+ {% for option in question_options %}
246
+ let value = parseInt(document.getElementById("{{ option }}").value) || 0;
247
+ if (value > 0 && value <= options && !values.includes(value)) {
248
+ values.push(value);
249
+ } else if (value !== 0) {
250
+ isValid = false;
251
+ }
252
+ {% endfor %}
253
+
254
+ if (!isValid || values.length !== new Set(values).size) {
255
+ document.getElementById("error").innerText = "Please enter unique and valid ranks for each option.";
256
+ } else {
257
+ document.getElementById("error").innerText = "";
258
+ }
84
259
  }
85
- return answer
260
+ </script>
261
+ <p id="error" style="color: red;"></p>
262
+ """
263
+ ).render(
264
+ question_name=self.question_name,
265
+ question_text=self.question_text,
266
+ question_options=self.question_options,
267
+ )
268
+ return question_html_content
86
269
 
87
270
  ################
88
271
  # Helpful methods
89
272
  ################
90
273
  @classmethod
91
- def example(cls) -> QuestionRank:
274
+ def example(cls, use_code=False, include_comment=True) -> QuestionRank:
275
+ """Return an example question."""
92
276
  return cls(
93
277
  question_name="rank_foods",
94
278
  question_text="Rank your favorite foods.",
95
279
  question_options=["Pizza", "Pasta", "Salad", "Soup"],
96
280
  num_selections=2,
281
+ use_code=use_code,
282
+ include_comment=include_comment,
97
283
  )
98
284
 
99
285
 
100
286
  def main():
287
+ """Show example usage."""
101
288
  from edsl.questions.QuestionRank import QuestionRank
102
289
 
103
- q = QuestionRank.example()
290
+ q = QuestionRank.example(use_code=True)
104
291
  q.question_text
105
292
  q.question_name
106
293
  q.question_options
107
294
  q.num_selections
108
295
  # validate an answer
109
296
  answer = {"answer": [0, 1], "comment": "I like pizza and pasta."}
110
- q.validate_answer(answer)
297
+ q._validate_answer(answer)
111
298
  # translate an answer code to an answer
112
- q.translate_answer_code_to_answer([0, 1])
299
+ # q._translate_answer_code_to_answer([0, 1])
113
300
  # simulate answer
114
- q.simulate_answer()
115
- q.simulate_answer(human_readable=False)
116
- q.validate_answer(q.simulate_answer(human_readable=False))
301
+ q._simulate_answer()
302
+ q._simulate_answer(human_readable=False)
303
+ q._validate_answer(q._simulate_answer(human_readable=False))
117
304
  # serialization (inherits from Question)
118
305
  q.to_dict()
119
306
  assert q.from_dict(q.to_dict()) == q
307
+
308
+ q = QuestionRank.example(use_code=False)
309
+ answer = {"answer": ["Pizza", "Pasta"], "comment": "I like pizza and pasta."}
310
+ q._validate_answer(answer)
311
+
312
+ import doctest
313
+
314
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -0,0 +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)