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,221 @@
1
+ from importlib import resources
2
+ from typing import Optional
3
+ from edsl.exceptions.questions import QuestionAnswerValidationError
4
+ from functools import lru_cache
5
+
6
+
7
+ class TemplateManager:
8
+ _instance = None
9
+
10
+ def __new__(cls):
11
+ if cls._instance is None:
12
+ cls._instance = super().__new__(cls)
13
+ cls._instance._template_cache = {}
14
+ return cls._instance
15
+
16
+ @lru_cache(maxsize=None)
17
+ def get_template(self, question_type, template_name):
18
+ if (question_type, template_name) not in self._template_cache:
19
+ with resources.open_text(
20
+ f"edsl.questions.templates.{question_type}", template_name
21
+ ) as file:
22
+ self._template_cache[(question_type, template_name)] = file.read()
23
+ return self._template_cache[(question_type, template_name)]
24
+
25
+
26
+ # Global instance
27
+ template_manager = TemplateManager()
28
+
29
+
30
+ class QuestionBasePromptsMixin:
31
+ @property
32
+ def model_instructions(self) -> dict:
33
+ """Get the model-specific instructions for the question."""
34
+ if not hasattr(self, "_model_instructions"):
35
+ self._model_instructions = {}
36
+ return self._model_instructions
37
+
38
+ def _all_text(self) -> str:
39
+ """Return the question text.
40
+
41
+ >>> from edsl import QuestionMultipleChoice as Q
42
+ >>> Q.example()._all_text()
43
+ "how_feelingHow are you?['Good', 'Great', 'OK', 'Bad']"
44
+ """
45
+ txt = ""
46
+ for key, value in self.data.items():
47
+ if isinstance(value, str):
48
+ txt += value
49
+ elif isinstance(value, list):
50
+ txt += "".join(str(value))
51
+ return txt
52
+
53
+ @model_instructions.setter
54
+ def model_instructions(self, data: dict):
55
+ """Set the model-specific instructions for the question."""
56
+ self._model_instructions = data
57
+
58
+ def add_model_instructions(
59
+ self, *, instructions: str, model: Optional[str] = None
60
+ ) -> None:
61
+ """Add model-specific instructions for the question that override the default instructions.
62
+
63
+ :param instructions: The instructions to add. This is typically a jinja2 template.
64
+ :param model: The language model for this instruction.
65
+
66
+ >>> from edsl.questions import QuestionFreeText
67
+ >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
68
+ >>> q.add_model_instructions(instructions = "{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
69
+ >>> q.get_instructions(model = "gpt3")
70
+ Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
71
+ """
72
+ from edsl.language_models.model import Model
73
+
74
+ if not hasattr(self, "_model_instructions"):
75
+ self._model_instructions = {}
76
+ if model is None:
77
+ # if not model is passed, all the models are mapped to this instruction, including 'None'
78
+ self._model_instructions = {
79
+ model_name: instructions
80
+ for model_name in Model.available(name_only=True)
81
+ }
82
+ self._model_instructions.update({model: instructions})
83
+ else:
84
+ self._model_instructions.update({model: instructions})
85
+
86
+ @classmethod
87
+ def path_to_folder(cls) -> str:
88
+ return resources.files(f"edsl.questions.templates", cls.question_type)
89
+
90
+ @property
91
+ def response_model(self) -> type["BaseModel"]:
92
+ if self._response_model is not None:
93
+ return self._response_model
94
+ else:
95
+ return self.create_response_model()
96
+
97
+ @property
98
+ def use_code(self) -> bool:
99
+ if hasattr(self, "_use_code"):
100
+ return self._use_code
101
+ return True
102
+
103
+ @use_code.setter
104
+ def use_code(self, value: bool) -> None:
105
+ self._use_code = value
106
+
107
+ @property
108
+ def include_comment(self) -> bool:
109
+ if hasattr(self, "_include_comment"):
110
+ return self._include_comment
111
+ return True
112
+
113
+ @include_comment.setter
114
+ def include_comment(self, value: bool) -> None:
115
+ self._include_comment = value
116
+
117
+ @classmethod
118
+ def default_answering_instructions(cls) -> str:
119
+ # template_text = cls._read_template("answering_instructions.jinja")
120
+ template_text = template_manager.get_template(
121
+ cls.question_type, "answering_instructions.jinja"
122
+ )
123
+ from edsl.prompts import Prompt
124
+
125
+ return Prompt(text=template_text)
126
+
127
+ @classmethod
128
+ def default_question_presentation(cls):
129
+ template_text = template_manager.get_template(
130
+ cls.question_type, "question_presentation.jinja"
131
+ )
132
+ from edsl.prompts import Prompt
133
+
134
+ return Prompt(text=template_text)
135
+
136
+ @property
137
+ def answering_instructions(self) -> str:
138
+ if self._answering_instructions is None:
139
+ return self.default_answering_instructions()
140
+ return self._answering_instructions
141
+
142
+ @answering_instructions.setter
143
+ def answering_instructions(self, value) -> None:
144
+ self._answering_instructions = value
145
+
146
+ @property
147
+ def question_presentation(self):
148
+ if self._question_presentation is None:
149
+ return self.default_question_presentation()
150
+ return self._question_presentation
151
+
152
+ @question_presentation.setter
153
+ def question_presentation(self, value):
154
+ self._question_presentation = value
155
+
156
+ def prompt_preview(self, scenario=None, agent=None):
157
+ return self.new_default_instructions.render(
158
+ self.data
159
+ | {
160
+ "include_comment": getattr(self, "_include_comment", True),
161
+ "use_code": getattr(self, "_use_code", True),
162
+ }
163
+ | ({"scenario": scenario} or {})
164
+ | ({"agent": agent} or {})
165
+ )
166
+
167
+ @classmethod
168
+ def self_check(cls):
169
+ q = cls.example()
170
+ for answer, params in q.response_validator.valid_examples:
171
+ for key, value in params.items():
172
+ setattr(q, key, value)
173
+ q._validate_answer(answer)
174
+ for answer, params, reason in q.response_validator.invalid_examples:
175
+ for key, value in params.items():
176
+ setattr(q, key, value)
177
+ try:
178
+ q._validate_answer(answer)
179
+ except QuestionAnswerValidationError:
180
+ pass
181
+ else:
182
+ raise ValueError(f"Example {answer} should have failed for {reason}.")
183
+
184
+ @property
185
+ def new_default_instructions(self) -> "Prompt":
186
+ "This is set up as a property because there are mutable question values that determine how it is rendered."
187
+ from edsl.prompts import Prompt
188
+
189
+ return Prompt(self.question_presentation) + Prompt(self.answering_instructions)
190
+
191
+ @property
192
+ def parameters(self) -> set[str]:
193
+ """Return the parameters of the question."""
194
+ from jinja2 import Environment, meta
195
+
196
+ env = Environment()
197
+ # Parse the template
198
+ txt = self._all_text()
199
+ # txt = self.question_text
200
+ # if hasattr(self, "question_options"):
201
+ # txt += " ".join(self.question_options)
202
+ parsed_content = env.parse(txt)
203
+ # Extract undeclared variables
204
+ variables = meta.find_undeclared_variables(parsed_content)
205
+ # Return as a list
206
+ return set(variables)
207
+
208
+ def get_instructions(self, model: Optional[str] = None) -> type["PromptBase"]:
209
+ """Get the mathcing question-answering instructions for the question.
210
+
211
+ :param model: The language model to use.
212
+ """
213
+ from edsl.prompts.Prompt import Prompt
214
+
215
+ if model in self.model_instructions:
216
+ return Prompt(text=self.model_instructions[model])
217
+ else:
218
+ if hasattr(self, "new_default_instructions"):
219
+ return self.new_default_instructions
220
+ else:
221
+ return self.applicable_prompts(model)[0]()
@@ -1,33 +1,70 @@
1
1
  from __future__ import annotations
2
- import random
3
- import textwrap
4
- from typing import Any, Optional, Union
5
- from edsl.questions import Question
2
+ from typing import Any, Optional, Union, List
3
+
4
+ from pydantic import Field, BaseModel, validator
5
+
6
+ from edsl.questions.QuestionBase import QuestionBase
6
7
  from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
7
- from edsl.scenarios import Scenario
8
- from edsl.utilities import random_string
8
+ from edsl.questions.response_validator_abc import ResponseValidatorABC
9
+
10
+
11
+ class BudgetResponseValidator(ResponseValidatorABC):
12
+ valid_examples = []
13
+
14
+ invalid_examples = []
15
+
16
+ def fix(self, response, verbose=False):
17
+ if verbose:
18
+ print(f"Fixing list response: {response}")
19
+ answer = str(response.get("answer") or response.get("generated_tokens", ""))
20
+ if len(answer.split(",")) > 0:
21
+ return (
22
+ {"answer": answer.split(",")} | {"comment": response.get("comment")}
23
+ if "comment" in response
24
+ else {}
25
+ )
26
+
27
+
28
+ def create_budget_model(
29
+ budget_sum: float, permissive: bool, question_options: List[str]
30
+ ):
31
+ class BudgetResponse(BaseModel):
32
+ answer: List[float] = Field(
33
+ ...,
34
+ description="List of non-negative numbers representing budget allocation",
35
+ min_items=len(question_options),
36
+ max_items=len(question_options),
37
+ )
38
+ comment: Optional[str] = None
39
+ generated_tokens: Optional[str] = None
9
40
 
41
+ @validator("answer")
42
+ def validate_answer(cls, v):
43
+ if len(v) != len(question_options):
44
+ raise ValueError(f"Must provide {len(question_options)} values")
45
+ if any(x < 0 for x in v):
46
+ raise ValueError("All values must be non-negative")
47
+ total = sum(v)
48
+ if not permissive and total != budget_sum:
49
+ raise ValueError(f"Sum of numbers must equal {budget_sum}")
50
+ elif permissive and total > budget_sum:
51
+ raise ValueError(f"Sum of numbers cannot exceed {budget_sum}")
52
+ return v
10
53
 
11
- class QuestionBudget(Question):
12
- """
13
- This question asks the respondent to allocate a budget among options.
54
+ class Config:
55
+ extra = "forbid"
14
56
 
15
- Arguments:
16
- - `budget_sum` is the total amount of the budget to be allocated (positive integer)
17
- - `question_name` is the name of the question (string)
18
- - `question_options` are the options the user should allocated the budget to (list of strings)
19
- - `question_text` is the text of the question (string)
57
+ return BudgetResponse
20
58
 
21
- Optional arguments:
22
- - `instructions` are the instructions for the question (string). If not provided, the default instructions are used. To view them, run `QuestionBudget.default_instructions`
23
- - `short_names_dict` maps question_options to short names (dictionary mapping strings to strings)
24
59
 
25
- To generate an example, run `QuestionBudget.example()`
26
- """
60
+ class QuestionBudget(QuestionBase):
61
+ """This question prompts the agent to allocate a budget among options."""
27
62
 
28
63
  question_type = "budget"
29
64
  budget_sum: int = IntegerDescriptor(none_allowed=False)
30
- question_options: list[str] = QuestionOptionsDescriptor()
65
+ question_options: list[str] = QuestionOptionsDescriptor(q_budget=True)
66
+ _response_model = None
67
+ response_validator_class = BudgetResponseValidator
31
68
 
32
69
  def __init__(
33
70
  self,
@@ -35,96 +72,156 @@ class QuestionBudget(Question):
35
72
  question_text: str,
36
73
  question_options: list[str],
37
74
  budget_sum: int,
38
- short_names_dict: Optional[dict[str, str]] = None,
75
+ include_comment: bool = True,
76
+ question_presentation: Optional[str] = None,
77
+ answering_instructions: Optional[str] = None,
78
+ permissive: bool = False,
39
79
  ):
80
+ """Instantiate a new QuestionBudget.
81
+
82
+ :param question_name: The name of the question.
83
+ :param question_text: The text of the question.
84
+ :param question_options: The options for allocation of the budget sum.
85
+ :param budget_sum: The total amount of the budget to be allocated among the options.
86
+ """
40
87
  self.question_name = question_name
41
88
  self.question_text = question_text
42
89
  self.question_options = question_options
43
90
  self.budget_sum = budget_sum
44
- self.short_names_dict = short_names_dict or {}
91
+ self.question_presentation = question_presentation
92
+ self.answering_instructions = answering_instructions
93
+ self.permissive = permissive
94
+ self.include_comment = include_comment
95
+
96
+ def create_response_model(self):
97
+ return create_budget_model(
98
+ self.budget_sum, self.permissive, self.question_options
99
+ )
100
+
101
+ def _translate_answer_code_to_answer(
102
+ self, answer_code, combined_dict
103
+ ) -> list[dict]:
104
+ """
105
+ Translate the answer codes to the actual answers.
45
106
 
46
- ################
47
- # Answer methods
48
- ################
49
- def validate_answer(self, answer: dict[str, Any]) -> dict[str, Union[int, str]]:
50
- self.validate_answer_template_basic(answer)
51
- self.validate_answer_key_value(answer, "answer", dict)
52
- self.validate_answer_budget(answer)
53
- return answer
54
-
55
- def translate_answer_code_to_answer(
56
- self, answer_codes: dict[str, int], scenario: Scenario = None
57
- ):
58
- """Translates the answer codes to the actual answers.
59
107
  For example, for a budget question with options ["a", "b", "c"],
60
108
  the answer codes are 0, 1, and 2. The LLM will respond with 0.
61
109
  This code will translate that to "a".
62
110
  """
63
111
  translated_codes = []
64
- for answer_code, response in answer_codes.items():
65
- translated_codes.append({self.question_options[int(answer_code)]: response})
112
+ for answer_code, question_option in zip(answer_code, self.question_options):
113
+ translated_codes.append({question_option: answer_code})
66
114
 
67
115
  return translated_codes
68
116
 
69
- def simulate_answer(self, human_readable=True):
70
- "Simulates a valid answer for debugging purposes (what the validator expects)"
71
- if human_readable:
72
- keys = self.question_options
73
- else:
74
- keys = range(len(self.question_options))
75
- remaining_budget = self.budget_sum
76
- values = []
77
- for _ in range(len(self.question_options)):
78
- if _ == len(self.question_options) - 1:
79
- # Assign remaining budget to the last value
80
- values.append(remaining_budget)
81
- else:
82
- # Generate a random value between 0 and remaining budget
83
- value = random.randint(0, remaining_budget)
84
- values.append(value)
85
- remaining_budget -= value
86
- answer = dict(zip(keys, values))
87
- return {
88
- "answer": answer,
89
- "comment": random_string(),
117
+ # def _simulate_answer(self, human_readable=True):
118
+ # """Simulate a valid answer for debugging purposes (what the validator expects)."""
119
+ # from edsl.utilities.utilities import random_string
120
+
121
+ # if human_readable:
122
+ # keys = self.question_options
123
+ # else:
124
+ # keys = range(len(self.question_options))
125
+ # remaining_budget = self.budget_sum
126
+ # values = []
127
+ # for _ in range(len(self.question_options)):
128
+ # if _ == len(self.question_options) - 1:
129
+ # # Assign remaining budget to the last value
130
+ # values.append(remaining_budget)
131
+ # else:
132
+ # # Generate a random value between 0 and remaining budget
133
+ # value = random.randint(0, remaining_budget)
134
+ # values.append(value)
135
+ # remaining_budget -= value
136
+ # answer = dict(zip(keys, values))
137
+ # return {
138
+ # "answer": answer,
139
+ # "comment": random_string(),
140
+ # }
141
+
142
+ @property
143
+ def question_html_content(self) -> str:
144
+ from jinja2 import Template
145
+
146
+ question_html_content = Template(
147
+ """
148
+ <form id="budgetForm">
149
+ <p>Total Budget: {{ budget_sum }}</p>
150
+ <p>Remaining Budget: <span id="remainingBudget">{{ budget_sum }}</span></p>
151
+ {% for option in question_options %}
152
+ <div>
153
+ <label for="{{ option }}">{{ option }}</label>
154
+ <input type="number" id="{{ option }}" name="{{ question_name }}[{{ option }}]" value="0" min="0" max="{{ budget_sum }}" oninput="updateRemainingBudget()">
155
+ </div>
156
+ {% endfor %}
157
+ </form>
158
+ <script>
159
+ function updateRemainingBudget() {
160
+ let totalBudget = {{ budget_sum }};
161
+ let allocated = 0;
162
+
163
+ {% for option in question_options %}
164
+ allocated += parseInt(document.getElementById("{{ option }}").value) || 0;
165
+ {% endfor %}
166
+
167
+ let remaining = totalBudget - allocated;
168
+ document.getElementById('remainingBudget').innerText = remaining;
169
+
170
+ {% for option in question_options %}
171
+ document.getElementById("{{ option }}").max = remaining + parseInt(document.getElementById("{{ option }}").value);
172
+ {% endfor %}
90
173
  }
174
+ </script>
175
+ """
176
+ ).render(
177
+ question_name=self.question_name,
178
+ budget_sum=self.budget_sum,
179
+ question_options=self.question_options,
180
+ )
181
+ return question_html_content
91
182
 
92
183
  ################
93
184
  # Helpful methods
94
185
  ################
95
186
  @classmethod
96
- def example(cls) -> QuestionBudget:
187
+ def example(cls, include_comment: bool = True) -> QuestionBudget:
188
+ """Return an example of a budget question."""
97
189
  return cls(
98
190
  question_name="food_budget",
99
191
  question_text="How would you allocate $100?",
100
192
  question_options=["Pizza", "Ice Cream", "Burgers", "Salad"],
101
193
  budget_sum=100,
194
+ include_comment=include_comment,
102
195
  )
103
196
 
104
197
 
105
198
  def main():
199
+ """Create an example of a budget question and demonstrate its functionality."""
106
200
  from edsl.questions.QuestionBudget import QuestionBudget
107
201
 
108
202
  q = QuestionBudget.example()
109
203
  q.question_text
110
204
  q.question_options
111
205
  q.question_name
112
- q.short_names_dict
113
206
  # validate an answer
114
- q.validate_answer(
207
+ q._validate_answer(
115
208
  {"answer": {0: 100, 1: 0, 2: 0, 3: 0}, "comment": "I like custard"}
116
209
  )
117
210
  # translate answer code
118
- q.translate_answer_code_to_answer({0: 100, 1: 0, 2: 0, 3: 0})
211
+ q._translate_answer_code_to_answer({0: 100, 1: 0, 2: 0, 3: 0})
119
212
  # simulate answer
120
- q.simulate_answer()
121
- q.simulate_answer(human_readable=False)
122
- q.validate_answer(q.simulate_answer(human_readable=False))
213
+ q._simulate_answer()
214
+ q._simulate_answer(human_readable=False)
215
+ q._validate_answer(q._simulate_answer(human_readable=False))
123
216
  # serialization (inherits from Question)
124
217
  q.to_dict()
125
218
  assert q.from_dict(q.to_dict()) == q
126
219
 
127
220
 
128
221
  if __name__ == "__main__":
129
- q = QuestionBudget.example()
130
- results = q.run()
222
+ # q = QuestionBudget.example()
223
+ # results = q.run()
224
+
225
+ import doctest
226
+
227
+ doctest.testmod(optionflags=doctest.ELLIPSIS)