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,594 @@
1
+ """Mixin class for exporting results."""
2
+
3
+ import io
4
+ import warnings
5
+ import textwrap
6
+ from typing import Optional, Tuple, Union, List
7
+
8
+ from edsl.results.file_exports import CSVExport, ExcelExport, JSONLExport, SQLiteExport
9
+
10
+
11
+ class DatasetExportMixin:
12
+ """Mixin class for exporting Dataset objects."""
13
+
14
+ def relevant_columns(
15
+ self, data_type: Optional[str] = None, remove_prefix=False
16
+ ) -> list:
17
+ """Return the set of keys that are present in the dataset.
18
+
19
+ :param data_type: The data type to filter by.
20
+ :param remove_prefix: Whether to remove the prefix from the column names.
21
+
22
+ >>> from edsl.results.Dataset import Dataset
23
+ >>> d = Dataset([{'a.b':[1,2,3,4]}])
24
+ >>> d.relevant_columns()
25
+ ['a.b']
26
+
27
+ >>> d.relevant_columns(remove_prefix=True)
28
+ ['b']
29
+
30
+ >>> d = Dataset([{'a':[1,2,3,4]}, {'b':[5,6,7,8]}])
31
+ >>> d.relevant_columns()
32
+ ['a', 'b']
33
+
34
+ >>> from edsl.results import Results; Results.example().select('how_feeling', 'how_feeling_yesterday').relevant_columns()
35
+ ['answer.how_feeling', 'answer.how_feeling_yesterday']
36
+
37
+ >>> from edsl.results import Results
38
+ >>> sorted(Results.example().select().relevant_columns(data_type = "model"))
39
+ ['model.frequency_penalty', ...]
40
+
41
+ >>> Results.example().relevant_columns(data_type = "flimflam")
42
+ Traceback (most recent call last):
43
+ ...
44
+ ValueError: No columns found for data type: flimflam. Available data types are: ...
45
+ """
46
+ columns = [list(x.keys())[0] for x in self]
47
+ if remove_prefix:
48
+ columns = [column.split(".")[-1] for column in columns]
49
+
50
+ def get_data_type(column):
51
+ if "." in column:
52
+ return column.split(".")[0]
53
+ else:
54
+ return None
55
+
56
+ if data_type:
57
+ all_columns = columns[:]
58
+ columns = [
59
+ column for column in columns if get_data_type(column) == data_type
60
+ ]
61
+ if len(columns) == 0:
62
+ all_data_types = sorted(
63
+ list(set(get_data_type(column) for column in all_columns))
64
+ )
65
+ raise ValueError(
66
+ f"No columns found for data type: {data_type}. Available data types are: {all_data_types}."
67
+ )
68
+
69
+ return columns
70
+
71
+ def num_observations(self):
72
+ """Return the number of observations in the dataset.
73
+
74
+ >>> from edsl.results.Results import Results
75
+ >>> Results.example().num_observations()
76
+ 4
77
+ """
78
+ _num_observations = None
79
+ for entry in self:
80
+ key, values = list(entry.items())[0]
81
+ if _num_observations is None:
82
+ _num_observations = len(values)
83
+ else:
84
+ if len(values) != _num_observations:
85
+ raise ValueError(
86
+ "The number of observations is not consistent across columns."
87
+ )
88
+
89
+ return _num_observations
90
+
91
+ def _make_tabular(
92
+ self, remove_prefix: bool, pretty_labels: Optional[dict] = None
93
+ ) -> tuple[list, List[list]]:
94
+ """Turn the results into a tabular format.
95
+
96
+ :param remove_prefix: Whether to remove the prefix from the column names.
97
+
98
+ >>> from edsl.results import Results
99
+ >>> r = Results.example()
100
+ >>> r.select('how_feeling')._make_tabular(remove_prefix = True)
101
+ (['how_feeling'], [['OK'], ['Great'], ['Terrible'], ['OK']])
102
+
103
+ >>> r.select('how_feeling')._make_tabular(remove_prefix = True, pretty_labels = {'how_feeling': "How are you feeling"})
104
+ (['How are you feeling'], [['OK'], ['Great'], ['Terrible'], ['OK']])
105
+ """
106
+
107
+ def create_dict_from_list_of_dicts(list_of_dicts):
108
+ for entry in list_of_dicts:
109
+ key, list_of_values = list(entry.items())[0]
110
+ yield key, list_of_values
111
+
112
+ tabular_repr = dict(create_dict_from_list_of_dicts(self.data))
113
+
114
+ full_header = [list(x.keys())[0] for x in self]
115
+
116
+ rows = []
117
+ for i in range(self.num_observations()):
118
+ row = [tabular_repr[h][i] for h in full_header]
119
+ rows.append(row)
120
+
121
+ if remove_prefix:
122
+ header = [h.split(".")[-1] for h in full_header]
123
+ else:
124
+ header = full_header
125
+
126
+ if pretty_labels is not None:
127
+ header = [pretty_labels.get(h, h) for h in header]
128
+
129
+ return header, rows
130
+
131
+ def print_long(self):
132
+ """Print the results in a long format.
133
+ >>> from edsl.results import Results
134
+ >>> r = Results.example()
135
+ >>> r.select('how_feeling').print_long()
136
+ answer.how_feeling: OK
137
+ answer.how_feeling: Great
138
+ answer.how_feeling: Terrible
139
+ answer.how_feeling: OK
140
+ """
141
+ for entry in self:
142
+ key, list_of_values = list(entry.items())[0]
143
+ for value in list_of_values:
144
+ print(f"{key}: {value}")
145
+
146
+ def _get_tabular_data(
147
+ self,
148
+ remove_prefix: bool = False,
149
+ pretty_labels: Optional[dict] = None,
150
+ ) -> Tuple[List[str], List[List]]:
151
+ """Internal method to get tabular data in a standard format.
152
+
153
+ Args:
154
+ remove_prefix: Whether to remove the prefix from column names
155
+ pretty_labels: Dictionary mapping original column names to pretty labels
156
+
157
+ Returns:
158
+ Tuple containing (header_row, data_rows)
159
+ """
160
+ if pretty_labels is None:
161
+ pretty_labels = {}
162
+
163
+ return self._make_tabular(
164
+ remove_prefix=remove_prefix, pretty_labels=pretty_labels
165
+ )
166
+
167
+ def to_jsonl(self, filename: Optional[str] = None) -> Optional["FileStore"]:
168
+ """Export the results to a FileStore instance containing JSONL data."""
169
+ exporter = JSONLExport(data=self, filename=filename)
170
+ return exporter.export()
171
+
172
+ def to_sqlite(
173
+ self,
174
+ filename: Optional[str] = None,
175
+ remove_prefix: bool = False,
176
+ pretty_labels: Optional[dict] = None,
177
+ table_name: str = "results",
178
+ if_exists: str = "replace",
179
+ ) -> Optional["FileStore"]:
180
+ """Export the results to a SQLite database file."""
181
+ exporter = SQLiteExport(
182
+ data=self,
183
+ filename=filename,
184
+ remove_prefix=remove_prefix,
185
+ pretty_labels=pretty_labels,
186
+ table_name=table_name,
187
+ if_exists=if_exists,
188
+ )
189
+ return exporter.export()
190
+
191
+ def to_csv(
192
+ self,
193
+ filename: Optional[str] = None,
194
+ remove_prefix: bool = False,
195
+ pretty_labels: Optional[dict] = None,
196
+ ) -> Optional["FileStore"]:
197
+ """Export the results to a FileStore instance containing CSV data."""
198
+ exporter = CSVExport(
199
+ data=self,
200
+ filename=filename,
201
+ remove_prefix=remove_prefix,
202
+ pretty_labels=pretty_labels,
203
+ )
204
+ return exporter.export()
205
+
206
+ def to_excel(
207
+ self,
208
+ filename: Optional[str] = None,
209
+ remove_prefix: bool = False,
210
+ pretty_labels: Optional[dict] = None,
211
+ sheet_name: Optional[str] = None,
212
+ ) -> Optional["FileStore"]:
213
+ """Export the results to a FileStore instance containing Excel data."""
214
+ exporter = ExcelExport(
215
+ data=self,
216
+ filename=filename,
217
+ remove_prefix=remove_prefix,
218
+ pretty_labels=pretty_labels,
219
+ sheet_name=sheet_name,
220
+ )
221
+ return exporter.export()
222
+
223
+ def _db(self, remove_prefix: bool = True):
224
+ """Create a SQLite database in memory and return the connection.
225
+
226
+ Args:
227
+ shape: The shape of the data in the database (wide or long)
228
+ remove_prefix: Whether to remove the prefix from the column names
229
+
230
+ Returns:
231
+ A database connection
232
+ """
233
+ from sqlalchemy import create_engine
234
+
235
+ engine = create_engine("sqlite:///:memory:")
236
+ if remove_prefix:
237
+ df = self.remove_prefix().to_pandas(lists_as_strings=True)
238
+ else:
239
+ df = self.to_pandas(lists_as_strings=True)
240
+ df.to_sql(
241
+ "self",
242
+ engine,
243
+ index=False,
244
+ if_exists="replace",
245
+ )
246
+ return engine.connect()
247
+
248
+ def sql(
249
+ self,
250
+ query: str,
251
+ transpose: bool = None,
252
+ transpose_by: str = None,
253
+ remove_prefix: bool = True,
254
+ ) -> Union["pd.DataFrame", str]:
255
+ """Execute a SQL query and return the results as a DataFrame.
256
+
257
+ Args:
258
+ query: The SQL query to execute
259
+ shape: The shape of the data in the database (wide or long)
260
+ remove_prefix: Whether to remove the prefix from the column names
261
+ transpose: Whether to transpose the DataFrame
262
+ transpose_by: The column to use as the index when transposing
263
+ csv: Whether to return the DataFrame as a CSV string
264
+ to_list: Whether to return the results as a list
265
+ to_latex: Whether to return the results as LaTeX
266
+ filename: Optional filename to save the results to
267
+
268
+ Returns:
269
+ DataFrame, CSV string, list, or LaTeX string depending on parameters
270
+
271
+ """
272
+ import pandas as pd
273
+
274
+ conn = self._db(remove_prefix=remove_prefix)
275
+ df = pd.read_sql_query(query, conn)
276
+
277
+ # Transpose the DataFrame if transpose is True
278
+ if transpose or transpose_by:
279
+ df = pd.DataFrame(df)
280
+ if transpose_by:
281
+ df = df.set_index(transpose_by)
282
+ else:
283
+ df = df.set_index(df.columns[0])
284
+ df = df.transpose()
285
+ from edsl.results.Dataset import Dataset
286
+
287
+ return Dataset.from_pandas_dataframe(df)
288
+
289
+ def to_pandas(
290
+ self, remove_prefix: bool = False, lists_as_strings=False
291
+ ) -> "DataFrame":
292
+ """Convert the results to a pandas DataFrame, ensuring that lists remain as lists.
293
+
294
+ :param remove_prefix: Whether to remove the prefix from the column names.
295
+
296
+ """
297
+ return self._to_pandas_strings(remove_prefix)
298
+
299
+ def _to_pandas_strings(self, remove_prefix: bool = False) -> "pd.DataFrame":
300
+ """Convert the results to a pandas DataFrame.
301
+
302
+ :param remove_prefix: Whether to remove the prefix from the column names.
303
+
304
+ >>> from edsl.results import Results
305
+ >>> r = Results.example()
306
+ >>> r.select('how_feeling').to_pandas()
307
+ answer.how_feeling
308
+ 0 OK
309
+ 1 Great
310
+ 2 Terrible
311
+ 3 OK
312
+ """
313
+
314
+ import pandas as pd
315
+
316
+ csv_string = self.to_csv(remove_prefix=remove_prefix).text
317
+ csv_buffer = io.StringIO(csv_string)
318
+ df = pd.read_csv(csv_buffer)
319
+ # df_sorted = df.sort_index(axis=1) # Sort columns alphabetically
320
+ return df
321
+
322
+ def to_polars(
323
+ self, remove_prefix: bool = False, lists_as_strings=False
324
+ ) -> "pl.DataFrame":
325
+ """Convert the results to a Polars DataFrame.
326
+
327
+ :param remove_prefix: Whether to remove the prefix from the column names.
328
+ """
329
+ return self._to_polars_strings(remove_prefix)
330
+
331
+ def _to_polars_strings(self, remove_prefix: bool = False) -> "pl.DataFrame":
332
+ """Convert the results to a Polars DataFrame.
333
+
334
+ :param remove_prefix: Whether to remove the prefix from the column names.
335
+ """
336
+ import polars as pl
337
+
338
+ csv_string = self.to_csv(remove_prefix=remove_prefix).text
339
+ df = pl.read_csv(io.StringIO(csv_string))
340
+ return df
341
+
342
+ def to_scenario_list(self, remove_prefix: bool = True) -> list[dict]:
343
+ """Convert the results to a list of dictionaries, one per scenario.
344
+
345
+ :param remove_prefix: Whether to remove the prefix from the column names.
346
+
347
+ >>> from edsl.results import Results
348
+ >>> r = Results.example()
349
+ >>> r.select('how_feeling').to_scenario_list()
350
+ ScenarioList([Scenario({'how_feeling': 'OK'}), Scenario({'how_feeling': 'Great'}), Scenario({'how_feeling': 'Terrible'}), Scenario({'how_feeling': 'OK'})])
351
+ """
352
+ from edsl.scenarios.ScenarioList import ScenarioList
353
+ from edsl.scenarios.Scenario import Scenario
354
+
355
+ list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
356
+ scenarios = []
357
+ for d in list_of_dicts:
358
+ scenarios.append(Scenario(d))
359
+ return ScenarioList(scenarios)
360
+
361
+ def to_agent_list(self, remove_prefix: bool = True):
362
+ """Convert the results to a list of dictionaries, one per agent.
363
+
364
+ :param remove_prefix: Whether to remove the prefix from the column names.
365
+
366
+ >>> from edsl.results import Results
367
+ >>> r = Results.example()
368
+ >>> r.select('how_feeling').to_agent_list()
369
+ AgentList([Agent(traits = {'how_feeling': 'OK'}), Agent(traits = {'how_feeling': 'Great'}), Agent(traits = {'how_feeling': 'Terrible'}), Agent(traits = {'how_feeling': 'OK'})])
370
+ """
371
+ from edsl.agents import Agent
372
+ from edsl.agents.AgentList import AgentList
373
+
374
+ list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
375
+ agents = []
376
+ for d in list_of_dicts:
377
+ if "name" in d:
378
+ d["agent_name"] = d.pop("name")
379
+ agents.append(Agent(d, name=d["agent_name"]))
380
+ if "agent_parameters" in d:
381
+ agent_parameters = d.pop("agent_parameters")
382
+ agent_name = agent_parameters.get("name", None)
383
+ instruction = agent_parameters.get("instruction", None)
384
+ agents.append(Agent(d, name=agent_name, instruction=instruction))
385
+ else:
386
+ agents.append(Agent(d))
387
+ return AgentList(agents)
388
+
389
+ def to_dicts(self, remove_prefix: bool = True) -> list[dict]:
390
+ """Convert the results to a list of dictionaries.
391
+
392
+ :param remove_prefix: Whether to remove the prefix from the column names.
393
+
394
+ >>> from edsl.results import Results
395
+ >>> r = Results.example()
396
+ >>> r.select('how_feeling').to_dicts()
397
+ [{'how_feeling': 'OK'}, {'how_feeling': 'Great'}, {'how_feeling': 'Terrible'}, {'how_feeling': 'OK'}]
398
+
399
+ """
400
+ list_of_keys = []
401
+ list_of_values = []
402
+ for entry in self:
403
+ key, values = list(entry.items())[0]
404
+ list_of_keys.append(key)
405
+ list_of_values.append(values)
406
+
407
+ if remove_prefix:
408
+ list_of_keys = [key.split(".")[-1] for key in list_of_keys]
409
+
410
+ list_of_dicts = []
411
+ for entries in zip(*list_of_values):
412
+ list_of_dicts.append(dict(zip(list_of_keys, entries)))
413
+
414
+ return list_of_dicts
415
+
416
+ def to_list(self, flatten=False, remove_none=False, unzipped=False) -> list[list]:
417
+ """Convert the results to a list of lists.
418
+
419
+ :param flatten: Whether to flatten the list of lists.
420
+ :param remove_none: Whether to remove None values from the list.
421
+
422
+ >>> from edsl.results import Results
423
+ >>> Results.example().select('how_feeling', 'how_feeling_yesterday')
424
+ Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling_yesterday': ['Great', 'Good', 'OK', 'Terrible']}])
425
+
426
+ >>> Results.example().select('how_feeling', 'how_feeling_yesterday').to_list()
427
+ [('OK', 'Great'), ('Great', 'Good'), ('Terrible', 'OK'), ('OK', 'Terrible')]
428
+
429
+ >>> r = Results.example()
430
+ >>> r.select('how_feeling').to_list()
431
+ ['OK', 'Great', 'Terrible', 'OK']
432
+
433
+ >>> from edsl.results.Dataset import Dataset
434
+ >>> Dataset([{'a.b': [[1, 9], 2, 3, 4]}]).select('a.b').to_list(flatten = True)
435
+ [1, 9, 2, 3, 4]
436
+
437
+ >>> from edsl.results.Dataset import Dataset
438
+ >>> Dataset([{'a.b': [[1, 9], 2, 3, 4]}, {'c': [6, 2, 3, 4]}]).select('a.b', 'c').to_list(flatten = True)
439
+ Traceback (most recent call last):
440
+ ...
441
+ ValueError: Cannot flatten a list of lists when there are multiple columns selected.
442
+
443
+
444
+ """
445
+ if len(self.relevant_columns()) > 1 and flatten:
446
+ raise ValueError(
447
+ "Cannot flatten a list of lists when there are multiple columns selected."
448
+ )
449
+
450
+ if len(self.relevant_columns()) == 1:
451
+ # if only one 'column' is selected (which is typical for this method
452
+ list_to_return = list(self[0].values())[0]
453
+ else:
454
+ keys = self.relevant_columns()
455
+ data = self.to_dicts(remove_prefix=False)
456
+ list_to_return = []
457
+ for d in data:
458
+ list_to_return.append(tuple([d[key] for key in keys]))
459
+
460
+ if remove_none:
461
+ list_to_return = [item for item in list_to_return if item is not None]
462
+
463
+ if flatten:
464
+ new_list = []
465
+ for item in list_to_return:
466
+ if isinstance(item, list):
467
+ new_list.extend(item)
468
+ else:
469
+ new_list.append(item)
470
+ list_to_return = new_list
471
+
472
+ from edsl.utilities.PrettyList import PrettyList
473
+
474
+ return PrettyList(list_to_return)
475
+
476
+ def html(
477
+ self,
478
+ filename: Optional[str] = None,
479
+ cta: str = "Open in browser",
480
+ return_link: bool = False,
481
+ ):
482
+ import os
483
+ import tempfile
484
+ from edsl.utilities.utilities import is_notebook
485
+ from IPython.display import HTML, display
486
+ from edsl.utilities.utilities import is_notebook
487
+
488
+ df = self.to_pandas()
489
+
490
+ if filename is None:
491
+ current_directory = os.getcwd()
492
+ filename = tempfile.NamedTemporaryFile(
493
+ "w", delete=False, suffix=".html", dir=current_directory
494
+ ).name
495
+
496
+ with open(filename, "w") as f:
497
+ f.write(df.to_html())
498
+
499
+ if is_notebook():
500
+ html_url = f"/files/{filename}"
501
+ html_link = f'<a href="{html_url}" target="_blank">{cta}</a>'
502
+ display(HTML(html_link))
503
+ else:
504
+ print(f"Saved to {filename}")
505
+ import webbrowser
506
+ import os
507
+
508
+ webbrowser.open(f"file://{os.path.abspath(filename)}")
509
+
510
+ if return_link:
511
+ return filename
512
+
513
+ def tally(
514
+ self, *fields: Optional[str], top_n: Optional[int] = None, output="Dataset"
515
+ ) -> Union[dict, "Dataset"]:
516
+ """Tally the values of a field or perform a cross-tab of multiple fields.
517
+
518
+ :param fields: The field(s) to tally, multiple fields for cross-tabulation.
519
+
520
+ >>> from edsl.results import Results
521
+ >>> r = Results.example()
522
+ >>> r.select('how_feeling').tally('answer.how_feeling', output = "dict")
523
+ {'OK': 2, 'Great': 1, 'Terrible': 1}
524
+ >>> from edsl.results.Dataset import Dataset
525
+ >>> expected = Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
526
+ >>> r.select('how_feeling').tally('answer.how_feeling', output = "Dataset") == expected
527
+ True
528
+ """
529
+ from collections import Counter
530
+
531
+ if len(fields) == 0:
532
+ fields = self.relevant_columns()
533
+
534
+ relevant_columns_without_prefix = [
535
+ column.split(".")[-1] for column in self.relevant_columns()
536
+ ]
537
+
538
+ if not all(
539
+ f in self.relevant_columns() or f in relevant_columns_without_prefix
540
+ for f in fields
541
+ ):
542
+ raise ValueError("One or more specified fields are not in the dataset.")
543
+
544
+ if len(fields) == 1:
545
+ field = fields[0]
546
+ values = self._key_to_value(field)
547
+ else:
548
+ values = list(zip(*(self._key_to_value(field) for field in fields)))
549
+
550
+ for value in values:
551
+ if isinstance(value, list):
552
+ value = tuple(value)
553
+
554
+ tally = dict(Counter(values))
555
+ sorted_tally = dict(sorted(tally.items(), key=lambda item: -item[1]))
556
+ if top_n is not None:
557
+ sorted_tally = dict(list(sorted_tally.items())[:top_n])
558
+
559
+ from edsl.results.Dataset import Dataset
560
+
561
+ if output == "dict":
562
+ # why did I do this?
563
+ warnings.warn(
564
+ textwrap.dedent(
565
+ """\
566
+ The default output from tally will change to Dataset in the future.
567
+ Use output='Dataset' to get the Dataset object for now.
568
+ """
569
+ )
570
+ )
571
+ return sorted_tally
572
+ elif output == "Dataset":
573
+ dataset = Dataset(
574
+ [
575
+ {"value": list(sorted_tally.keys())},
576
+ {"count": list(sorted_tally.values())},
577
+ ]
578
+ )
579
+ # return dataset
580
+ sl = dataset.to_scenario_list().unpack(
581
+ "value",
582
+ new_names=[fields] if isinstance(fields, str) else fields,
583
+ keep_original=False,
584
+ )
585
+ keys = list(sl[0].keys())
586
+ keys.remove("count")
587
+ keys.append("count")
588
+ return sl.reorder_keys(keys).to_dataset()
589
+
590
+
591
+ if __name__ == "__main__":
592
+ import doctest
593
+
594
+ doctest.testmod(optionflags=doctest.ELLIPSIS)