edsl 0.1.46__py3-none-any.whl → 0.1.48__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 (328) hide show
  1. edsl/__init__.py +44 -39
  2. edsl/__version__.py +1 -1
  3. edsl/agents/__init__.py +4 -2
  4. edsl/agents/{Agent.py → agent.py} +442 -152
  5. edsl/agents/{AgentList.py → agent_list.py} +220 -162
  6. edsl/agents/descriptors.py +46 -7
  7. edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
  8. edsl/base/__init__.py +75 -0
  9. edsl/base/base_class.py +1303 -0
  10. edsl/base/data_transfer_models.py +114 -0
  11. edsl/base/enums.py +215 -0
  12. edsl/base.py +8 -0
  13. edsl/buckets/__init__.py +25 -0
  14. edsl/buckets/bucket_collection.py +324 -0
  15. edsl/buckets/model_buckets.py +206 -0
  16. edsl/buckets/token_bucket.py +502 -0
  17. edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
  18. edsl/buckets/token_bucket_client.py +509 -0
  19. edsl/caching/__init__.py +20 -0
  20. edsl/caching/cache.py +814 -0
  21. edsl/caching/cache_entry.py +427 -0
  22. edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
  23. edsl/caching/exceptions.py +24 -0
  24. edsl/caching/orm.py +30 -0
  25. edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
  26. edsl/caching/sql_dict.py +441 -0
  27. edsl/config/__init__.py +8 -0
  28. edsl/config/config_class.py +177 -0
  29. edsl/config.py +4 -176
  30. edsl/conversation/Conversation.py +7 -7
  31. edsl/conversation/car_buying.py +4 -4
  32. edsl/conversation/chips.py +6 -6
  33. edsl/coop/__init__.py +25 -2
  34. edsl/coop/coop.py +430 -113
  35. edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
  36. edsl/coop/exceptions.py +62 -0
  37. edsl/coop/price_fetcher.py +126 -0
  38. edsl/coop/utils.py +89 -24
  39. edsl/data_transfer_models.py +5 -72
  40. edsl/dataset/__init__.py +10 -0
  41. edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
  42. edsl/dataset/dataset_operations_mixin.py +1492 -0
  43. edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
  44. edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
  45. edsl/{results → dataset/display}/table_renderers.py +58 -2
  46. edsl/{results → dataset}/file_exports.py +4 -5
  47. edsl/{results → dataset}/smart_objects.py +2 -2
  48. edsl/enums.py +5 -205
  49. edsl/inference_services/__init__.py +5 -0
  50. edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
  51. edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
  52. edsl/inference_services/data_structures.py +3 -2
  53. edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
  54. edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
  55. edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
  56. edsl/inference_services/registry.py +4 -41
  57. edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
  58. edsl/inference_services/services/__init__.py +31 -0
  59. edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
  60. edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
  61. edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
  62. edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
  63. edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
  64. edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
  65. edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
  66. edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
  67. edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
  68. edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
  69. edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +12 -12
  70. edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
  71. edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
  72. edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
  73. edsl/inference_services/write_available.py +1 -2
  74. edsl/instructions/__init__.py +6 -0
  75. edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
  76. edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
  77. edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
  78. edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
  79. edsl/interviews/__init__.py +4 -0
  80. edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
  81. edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
  82. edsl/interviews/interview.py +638 -0
  83. edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
  84. edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
  85. edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
  86. edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
  87. edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
  88. edsl/invigilators/__init__.py +38 -0
  89. edsl/invigilators/invigilator_base.py +477 -0
  90. edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
  91. edsl/invigilators/prompt_constructor.py +476 -0
  92. edsl/{agents → invigilators}/prompt_helpers.py +2 -1
  93. edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
  94. edsl/{agents → invigilators}/question_option_processor.py +96 -21
  95. edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
  96. edsl/jobs/__init__.py +7 -1
  97. edsl/jobs/async_interview_runner.py +99 -35
  98. edsl/jobs/check_survey_scenario_compatibility.py +7 -5
  99. edsl/jobs/data_structures.py +153 -22
  100. edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
  101. edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
  102. edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
  103. edsl/jobs/{Jobs.py → jobs.py} +321 -155
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +20 -17
  106. edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
  107. edsl/jobs/jobs_pricing_estimation.py +347 -0
  108. edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
  109. edsl/jobs/jobs_runner_asyncio.py +282 -0
  110. edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
  111. edsl/jobs/results_exceptions_handler.py +2 -2
  112. edsl/key_management/__init__.py +28 -0
  113. edsl/key_management/key_lookup.py +161 -0
  114. edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
  115. edsl/key_management/key_lookup_collection.py +82 -0
  116. edsl/key_management/models.py +218 -0
  117. edsl/language_models/__init__.py +7 -2
  118. edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
  119. edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
  120. edsl/language_models/language_model.py +1080 -0
  121. edsl/language_models/model.py +10 -25
  122. edsl/language_models/{ModelList.py → model_list.py} +9 -14
  123. edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
  124. edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
  125. edsl/language_models/repair.py +4 -4
  126. edsl/language_models/utilities.py +4 -4
  127. edsl/notebooks/__init__.py +3 -1
  128. edsl/notebooks/{Notebook.py → notebook.py} +7 -8
  129. edsl/prompts/__init__.py +1 -1
  130. edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
  131. edsl/prompts/{Prompt.py → prompt.py} +101 -95
  132. edsl/questions/HTMLQuestion.py +1 -1
  133. edsl/questions/__init__.py +154 -25
  134. edsl/questions/answer_validator_mixin.py +1 -1
  135. edsl/questions/compose_questions.py +4 -3
  136. edsl/questions/derived/question_likert_five.py +166 -0
  137. edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
  138. edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
  139. edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
  140. edsl/questions/descriptors.py +24 -30
  141. edsl/questions/loop_processor.py +65 -19
  142. edsl/questions/question_base.py +881 -0
  143. edsl/questions/question_base_gen_mixin.py +15 -16
  144. edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
  145. edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
  146. edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
  147. edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
  148. edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
  149. edsl/questions/question_free_text.py +282 -0
  150. edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
  151. edsl/questions/{QuestionList.py → question_list.py} +6 -7
  152. edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
  153. edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
  154. edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
  155. edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
  156. edsl/questions/question_registry.py +10 -16
  157. edsl/questions/register_questions_meta.py +8 -4
  158. edsl/questions/response_validator_abc.py +17 -16
  159. edsl/results/__init__.py +4 -1
  160. edsl/{exceptions/results.py → results/exceptions.py} +1 -1
  161. edsl/results/report.py +197 -0
  162. edsl/results/{Result.py → result.py} +131 -45
  163. edsl/results/{Results.py → results.py} +420 -216
  164. edsl/results/results_selector.py +344 -25
  165. edsl/scenarios/__init__.py +30 -3
  166. edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
  167. edsl/scenarios/directory_scanner.py +156 -13
  168. edsl/scenarios/document_chunker.py +186 -0
  169. edsl/scenarios/exceptions.py +101 -0
  170. edsl/scenarios/file_methods.py +2 -3
  171. edsl/scenarios/file_store.py +755 -0
  172. edsl/scenarios/handlers/__init__.py +14 -14
  173. edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
  174. edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
  175. edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
  176. edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
  177. edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
  178. edsl/scenarios/handlers/latex_file_store.py +5 -0
  179. edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
  180. edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
  181. edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
  182. edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
  183. edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
  184. edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
  185. edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
  186. edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
  187. edsl/scenarios/scenario.py +928 -0
  188. edsl/scenarios/scenario_join.py +18 -5
  189. edsl/scenarios/{ScenarioList.py → scenario_list.py} +424 -106
  190. edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
  191. edsl/scenarios/scenario_selector.py +5 -1
  192. edsl/study/ObjectEntry.py +2 -2
  193. edsl/study/SnapShot.py +5 -5
  194. edsl/study/Study.py +20 -21
  195. edsl/study/__init__.py +6 -4
  196. edsl/surveys/__init__.py +7 -4
  197. edsl/surveys/dag/__init__.py +2 -0
  198. edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
  199. edsl/surveys/{DAG.py → dag/dag.py} +13 -10
  200. edsl/surveys/descriptors.py +1 -1
  201. edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
  202. edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
  203. edsl/surveys/memory/__init__.py +3 -0
  204. edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
  205. edsl/surveys/rules/__init__.py +3 -0
  206. edsl/surveys/{Rule.py → rules/rule.py} +103 -43
  207. edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
  208. edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
  209. edsl/surveys/survey.py +1743 -0
  210. edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
  211. edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
  212. edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
  213. edsl/tasks/__init__.py +32 -0
  214. edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
  215. edsl/tasks/task_creators.py +135 -0
  216. edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
  217. edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
  218. edsl/tasks/task_status_log.py +85 -0
  219. edsl/tokens/__init__.py +2 -0
  220. edsl/tokens/interview_token_usage.py +53 -0
  221. edsl/utilities/PrettyList.py +1 -1
  222. edsl/utilities/SystemInfo.py +25 -22
  223. edsl/utilities/__init__.py +29 -21
  224. edsl/utilities/gcp_bucket/__init__.py +2 -0
  225. edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
  226. edsl/utilities/interface.py +44 -536
  227. edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
  228. edsl/utilities/repair_functions.py +1 -1
  229. {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/METADATA +3 -2
  230. edsl-0.1.48.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -426
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/auto/AutoStudy.py +0 -130
  236. edsl/auto/StageBase.py +0 -243
  237. edsl/auto/StageGenerateSurvey.py +0 -178
  238. edsl/auto/StageLabelQuestions.py +0 -125
  239. edsl/auto/StagePersona.py +0 -61
  240. edsl/auto/StagePersonaDimensionValueRanges.py +0 -88
  241. edsl/auto/StagePersonaDimensionValues.py +0 -74
  242. edsl/auto/StagePersonaDimensions.py +0 -69
  243. edsl/auto/StageQuestions.py +0 -74
  244. edsl/auto/SurveyCreatorPipeline.py +0 -21
  245. edsl/auto/utilities.py +0 -218
  246. edsl/base/Base.py +0 -279
  247. edsl/coop/PriceFetcher.py +0 -54
  248. edsl/data/Cache.py +0 -580
  249. edsl/data/CacheEntry.py +0 -230
  250. edsl/data/SQLiteDict.py +0 -292
  251. edsl/data/__init__.py +0 -5
  252. edsl/data/orm.py +0 -10
  253. edsl/exceptions/cache.py +0 -5
  254. edsl/exceptions/coop.py +0 -14
  255. edsl/exceptions/data.py +0 -14
  256. edsl/exceptions/scenarios.py +0 -29
  257. edsl/jobs/Answers.py +0 -43
  258. edsl/jobs/JobsPrompts.py +0 -354
  259. edsl/jobs/buckets/BucketCollection.py +0 -134
  260. edsl/jobs/buckets/ModelBuckets.py +0 -65
  261. edsl/jobs/buckets/TokenBucket.py +0 -283
  262. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  263. edsl/jobs/interviews/Interview.py +0 -395
  264. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  265. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  266. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  267. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  268. edsl/jobs/tasks/TaskCreators.py +0 -64
  269. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  270. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  271. edsl/language_models/LanguageModel.py +0 -635
  272. edsl/language_models/ServiceDataSources.py +0 -0
  273. edsl/language_models/key_management/KeyLookup.py +0 -63
  274. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  275. edsl/language_models/key_management/models.py +0 -137
  276. edsl/questions/QuestionBase.py +0 -539
  277. edsl/questions/QuestionFreeText.py +0 -130
  278. edsl/questions/derived/QuestionLikertFive.py +0 -76
  279. edsl/results/DatasetExportMixin.py +0 -911
  280. edsl/results/ResultsExportMixin.py +0 -45
  281. edsl/results/TextEditor.py +0 -50
  282. edsl/results/results_fetch_mixin.py +0 -33
  283. edsl/results/results_tools_mixin.py +0 -98
  284. edsl/scenarios/DocumentChunker.py +0 -104
  285. edsl/scenarios/FileStore.py +0 -564
  286. edsl/scenarios/Scenario.py +0 -548
  287. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  288. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  289. edsl/scenarios/handlers/latex.py +0 -5
  290. edsl/shared.py +0 -1
  291. edsl/surveys/Survey.py +0 -1306
  292. edsl/surveys/SurveyQualtricsImport.py +0 -284
  293. edsl/surveys/SurveyToApp.py +0 -141
  294. edsl/surveys/instructions/__init__.py +0 -0
  295. edsl/tools/__init__.py +0 -1
  296. edsl/tools/clusters.py +0 -192
  297. edsl/tools/embeddings.py +0 -27
  298. edsl/tools/embeddings_plotting.py +0 -118
  299. edsl/tools/plotting.py +0 -112
  300. edsl/tools/summarize.py +0 -18
  301. edsl/utilities/data/Registry.py +0 -6
  302. edsl/utilities/data/__init__.py +0 -1
  303. edsl/utilities/data/scooter_results.json +0 -1
  304. edsl-0.1.46.dist-info/RECORD +0 -366
  305. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  306. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  307. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  308. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  309. /edsl/{results → dataset/display}/table_display.css +0 -0
  310. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  311. /edsl/{results → dataset}/tree_explore.py +0 -0
  312. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  313. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  314. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  315. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  316. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  317. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  318. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  319. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  320. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  321. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  322. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  323. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  324. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  325. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  326. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  327. {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
  328. {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -1,4 +1,4 @@
1
- """A mixin class for exporting surveys to different formats."""
1
+ """A class for exporting surveys to different formats."""
2
2
 
3
3
  from typing import Union, Optional
4
4
 
@@ -24,11 +24,15 @@ def open_docx(file_path):
24
24
  subprocess.call(("xdg-open", file_path))
25
25
 
26
26
 
27
- class SurveyExportMixin:
28
- """A mixin class for exporting surveys to different formats."""
27
+ class SurveyExport:
28
+ """A class for exporting surveys to different formats."""
29
+
30
+ def __init__(self, survey):
31
+ """Initialize with a Survey object."""
32
+ self.survey = survey
29
33
 
30
34
  def css(self):
31
- from edsl.surveys.SurveyCSS import SurveyCSS
35
+ from .survey_css import SurveyCSS
32
36
 
33
37
  return SurveyCSS.default_style().generate_css()
34
38
 
@@ -36,7 +40,7 @@ class SurveyExportMixin:
36
40
  """Return the description of the survey."""
37
41
  from edsl import QuestionFreeText
38
42
 
39
- question_texts = "\n".join([q.question_text for q in self._questions])
43
+ question_texts = "\n".join([q.question_text for q in self.survey._questions])
40
44
  q = QuestionFreeText(
41
45
  question_name="description",
42
46
  question_text=f"""A survey was conducted with the following questions:
@@ -58,7 +62,7 @@ class SurveyExportMixin:
58
62
  doc = Document()
59
63
  doc.add_heading("EDSL Survey")
60
64
  doc.add_paragraph(f"\n")
61
- for index, question in enumerate(self._questions):
65
+ for index, question in enumerate(self.survey._questions):
62
66
  h = doc.add_paragraph() # Add question as a paragraph
63
67
  h.add_run(f"Question {index + 1} ({question.question_name})").bold = True
64
68
  h.add_run(f"; {question.question_type}").italic = True
@@ -72,20 +76,13 @@ class SurveyExportMixin:
72
76
  for option in getattr(question, "question_options", []):
73
77
  doc.add_paragraph(str(option), style="ListBullet")
74
78
 
75
- if return_document_object and filename is None:
79
+ if return_document_object:
76
80
  return doc
77
-
78
- if filename is None:
79
- with tempfile.NamedTemporaryFile(
80
- "w", delete=False, suffix=".docx", dir=os.getcwd()
81
- ) as f:
82
- filename = f.name
83
-
84
- doc.save(filename)
85
- print("The survey has been saved to", filename)
86
- if open_file:
87
- open_docx(filename)
88
- return
81
+ else:
82
+ doc.save(filename)
83
+ if open_file:
84
+ os.system(f"open {filename}")
85
+ return None
89
86
 
90
87
  def show(self):
91
88
  self.to_scenario_list(questions_only=False, rename=True).print(format="rich")
@@ -93,14 +90,12 @@ class SurveyExportMixin:
93
90
  def to_scenario_list(
94
91
  self, questions_only: bool = True, rename=False
95
92
  ) -> "ScenarioList":
96
- from edsl import ScenarioList, Scenario
97
-
98
- # from edsl.questions import QuestionBase
93
+ from ..scenarios import ScenarioList, Scenario
99
94
 
100
95
  if questions_only:
101
- to_iterate_over = self._questions
96
+ to_iterate_over = self.survey._questions
102
97
  else:
103
- to_iterate_over = self.recombined_questions_and_instructions()
98
+ to_iterate_over = self.survey.recombined_questions_and_instructions()
104
99
 
105
100
  if rename:
106
101
  renaming_dict = {
@@ -150,13 +145,13 @@ class SurveyExportMixin:
150
145
  header_lines = ["from edsl.surveys.Survey import Survey"]
151
146
  header_lines.append("from edsl import Question")
152
147
  lines = ["\n".join(header_lines)]
153
- for question in self._questions:
148
+ for question in self.survey._questions:
154
149
  question.question_text = question["question_text"].replace("\n", " ")
155
150
  # remove dublicate spaces
156
151
  question.question_text = " ".join(question.question_text.split())
157
152
  lines.append(f"{question.question_name} = " + repr(question))
158
153
  lines.append(
159
- f"{survey_var_name} = Survey(questions = [{', '.join(self.question_names)}])"
154
+ f"{survey_var_name} = Survey(questions = [{', '.join(self.survey.question_names)}])"
160
155
  )
161
156
  # return lines
162
157
  code_string = "\n".join(lines)
@@ -216,7 +211,7 @@ class SurveyExportMixin:
216
211
 
217
212
  with open(filename, "w") as f:
218
213
  f.write(html_header)
219
- for question in self._questions:
214
+ for question in self.survey._questions:
220
215
  f.write(
221
216
  question.html(
222
217
  scenario=scenario, include_question_name=include_question_name
@@ -47,7 +47,12 @@ class SurveyFlowVisualization:
47
47
  for param in question.detailed_parameters:
48
48
  if "agent." in param:
49
49
  # Handle agent trait references
50
- trait_name = param.replace("agent.", "")
50
+ #trait_name = param.replace("agent.", "")
51
+ params_and_refs.add(param)
52
+ if param not in param_to_questions:
53
+ param_to_questions[param] = []
54
+ param_to_questions[param].append(index)
55
+ if "scenario." in param:
51
56
  params_and_refs.add(param)
52
57
  if param not in param_to_questions:
53
58
  param_to_questions[param] = []
@@ -108,7 +113,7 @@ class SurveyFlowVisualization:
108
113
  "label": f"Agent Trait\n{{{{ {param} }}}}"
109
114
  })
110
115
  # Check if parameter exists in scenario
111
- elif self.scenario and param in self.scenario:
116
+ elif self.scenario and param.startswith("scenario."):
112
117
  node_attrs.update({
113
118
  "fillcolor": "lightgreen",
114
119
  "label": f"Scenario\n{{{{ {param} }}}}"
@@ -207,6 +212,10 @@ class SurveyFlowVisualization:
207
212
  """File not found. Most likely it's because you don't have graphviz installed. Please install it and try again.
208
213
  On Ubuntu, you can install it by running:
209
214
  $ sudo apt-get install graphviz
215
+ On Mac, you can install it by running:
216
+ $ brew install graphviz
217
+ On Windows, you can install it by running:
218
+ $ choco install graphviz
210
219
  """
211
220
  )
212
221
  from edsl.utilities.is_notebook import is_notebook
@@ -1,8 +1,15 @@
1
+ from typing import TYPE_CHECKING
1
2
  from typing import Callable
3
+ from edsl.agents import Agent
4
+ #from edsl.surveys import Survey
2
5
 
6
+ if TYPE_CHECKING:
7
+ from edsl.surveys import Survey
8
+ from edsl.results import Results
9
+ from edsl.questions import QuestionBase
3
10
 
4
11
  class Simulator:
5
- def __init__(self, survey):
12
+ def __init__(self, survey: "Survey"):
6
13
  self.survey = survey
7
14
 
8
15
  @classmethod
@@ -10,7 +17,7 @@ class Simulator:
10
17
  """Create a random survey."""
11
18
  from edsl.questions import QuestionMultipleChoice, QuestionFreeText
12
19
  from random import choice
13
- from edsl.surveys.Survey import Survey
20
+ from edsl.surveys import Survey
14
21
 
15
22
  num_questions = 10
16
23
  questions = []
@@ -57,7 +64,7 @@ class Simulator:
57
64
  def create_agent(self) -> "Agent":
58
65
  """Create an agent from the simulated answers."""
59
66
  answers_dict = self.survey.simulate()
60
- from edsl.agents.Agent import Agent
67
+ from edsl.agents import Agent
61
68
 
62
69
  def construct_answer_dict_function(traits: dict) -> Callable:
63
70
  def func(self, question: "QuestionBase", scenario=None):
edsl/tasks/__init__.py ADDED
@@ -0,0 +1,32 @@
1
+ """
2
+ The tasks module provides EDSL's task management system for tracking and controlling
3
+ interview execution.
4
+
5
+ This module implements a comprehensive system for creating, scheduling, executing, and
6
+ monitoring tasks in EDSL. Tasks represent individual units of work, typically answering
7
+ a question with an LLM, with features for dependency management, error handling, and
8
+ execution status tracking.
9
+
10
+ Key components:
11
+
12
+ 1. TaskHistory - Records and analyzes the execution history of tasks with error reporting
13
+ 2. QuestionTaskCreator - Creates and manages tasks for individual questions
14
+ 3. TaskCreators - Manages collections of tasks for an entire interview
15
+ 4. TaskStatus - Enumeration of possible task states (running, waiting, success, etc.)
16
+ 5. TaskStatusLog - Records the status changes of tasks over time
17
+
18
+ The tasks system helps EDSL manage complex interview workflows by:
19
+ - Handling dependencies between questions
20
+ - Managing API rate limits and token usage
21
+ - Providing detailed execution metrics
22
+ - Generating error reports and visualizations
23
+ - Supporting both synchronous and asynchronous execution
24
+
25
+ For most users, this module works behind the scenes, but understanding it can
26
+ be helpful when debugging or optimizing complex EDSL workflows.
27
+ """
28
+
29
+ from .task_history import TaskHistory
30
+ from .question_task_creator import QuestionTaskCreator
31
+ from .task_creators import TaskCreators
32
+ from .task_status_enum import TaskStatus, TaskStatusDescriptor
@@ -1,34 +1,77 @@
1
+ """
2
+ This module provides the QuestionTaskCreator class for executing individual questions as tasks.
3
+
4
+ The QuestionTaskCreator is responsible for executing a single question within the EDSL system.
5
+ It manages the entire lifecycle of a question task, including dependency resolution, rate
6
+ limiting, token management, execution, and status tracking. It serves as the fundamental
7
+ execution unit in EDSL's task system.
8
+ """
9
+
1
10
  import asyncio
2
- from typing import Callable, Union, List, TYPE_CHECKING
11
+ from typing import Callable, Union, List, Dict, Any, Optional, TYPE_CHECKING
3
12
  from collections import UserList, UserDict
4
13
 
5
- from edsl.exceptions.jobs import InterviewErrorPriorTaskCanceled
14
+ from ..jobs.exceptions import InterviewErrorPriorTaskCanceled
15
+ from ..tokens import TokenUsage
16
+ from ..data_transfer_models import Answers
6
17
 
7
- from edsl.jobs.tasks.task_status_enum import TaskStatus, TaskStatusDescriptor
8
- from edsl.jobs.tasks.TaskStatusLog import TaskStatusLog
9
- from edsl.jobs.tokens.TokenUsage import TokenUsage
10
- from edsl.jobs.Answers import Answers
18
+ from .task_status_enum import TaskStatus, TaskStatusDescriptor
19
+ from .task_status_log import TaskStatusLog
11
20
 
12
21
  if TYPE_CHECKING:
13
- from edsl.questions.QuestionBase import QuestionBase
14
- from edsl.jobs.buckets import ModelBuckets
22
+ from ..questions import QuestionBase
23
+ from ..buckets import ModelBuckets
15
24
 
16
25
 
17
26
  class TokensUsed(UserDict):
18
- """ "Container for tokens used by a task."""
27
+ """
28
+ Container for tracking token usage for a task, separating cached and new tokens.
29
+
30
+ This class provides a structured way to track token usage for a single task,
31
+ distinguishing between tokens reused from cache and tokens freshly generated.
32
+ It uses a UserDict interface for convenient access to the underlying data.
33
+
34
+ Attributes:
35
+ cached_tokens: TokenUsage object tracking reused tokens from cache
36
+ new_tokens: TokenUsage object tracking freshly generated tokens
37
+ """
19
38
 
20
- def __init__(self, cached_tokens, new_tokens):
39
+ def __init__(self, cached_tokens: TokenUsage, new_tokens: TokenUsage):
40
+ """
41
+ Initialize a TokensUsed container.
42
+
43
+ Parameters:
44
+ cached_tokens: TokenUsage object for tokens reused from cache
45
+ new_tokens: TokenUsage object for newly generated tokens
46
+ """
21
47
  d = {"cached_tokens": cached_tokens, "new_tokens": new_tokens}
22
48
  super().__init__(d)
23
49
 
24
50
 
25
51
  class QuestionTaskCreator(UserList):
26
- """Class to create and manage a single question and its dependencies.
27
-
28
- It is a UserList with all the tasks that must be completed before the focal task can be run.
29
- The focal task is the question that we are interested in answering.
30
52
  """
31
-
53
+ Creates and manages the execution of a single question as an asyncio task.
54
+
55
+ The QuestionTaskCreator is a fundamental component of EDSL's task system,
56
+ responsible for executing a single question with its dependencies. It extends
57
+ UserList to maintain a list of dependent tasks that must complete before this
58
+ task can execute.
59
+
60
+ Key responsibilities:
61
+ 1. Task Dependency Management - Tracks prerequisite tasks that must complete first
62
+ 2. Resource Management - Handles rate limiting and token quota management
63
+ 3. Task Status Tracking - Monitors and logs task state transitions
64
+ 4. Token Usage Tracking - Records token consumption for both cached and new tokens
65
+ 5. Task Execution - Runs the question answering function when dependencies are met
66
+
67
+ The class follows the state machine pattern, with task_status transitioning through
68
+ various TaskStatus states (NOT_STARTED, WAITING_FOR_DEPENDENCIES, etc.) as execution
69
+ progresses. All status changes are automatically logged to enable detailed analysis
70
+ and visualization.
71
+
72
+ This class is designed to work with asyncio for concurrent task execution, enabling
73
+ efficient processing of interviews with multiple questions and dependencies.
74
+ """
32
75
  task_status = TaskStatusDescriptor()
33
76
 
34
77
  def __init__(
@@ -37,17 +80,24 @@ class QuestionTaskCreator(UserList):
37
80
  question: "QuestionBase",
38
81
  answer_question_func: Callable,
39
82
  model_buckets: "ModelBuckets",
40
- token_estimator: Union[Callable, None] = None,
83
+ token_estimator: Optional[Callable] = None,
41
84
  iteration: int = 0,
42
85
  ):
43
- """Initialize the QuestionTaskCreator instance.
44
-
45
- :param question: the question that we are interested in answering.
46
- :param answer_question_func: the function that will answer the question.
47
- :param model_buckets: the bucket collection that contains the requests and tokens buckets which control the rate of API calls and token usage.
48
- :param token_estimator: a function that estimates the number of tokens required to answer the question.
49
- :param iteration: the iteration number of the question.
50
-
86
+ """
87
+ Initialize a QuestionTaskCreator for a specific question.
88
+
89
+ Parameters:
90
+ question: The Question object to be answered
91
+ answer_question_func: Function that will execute the LLM call to answer the question
92
+ model_buckets: Container for rate limiting buckets (requests and tokens)
93
+ token_estimator: Function to estimate token usage for the question (for quota management)
94
+ iteration: The iteration number of this question (for repeated questions)
95
+
96
+ Notes:
97
+ - The QuestionTaskCreator starts in the NOT_STARTED state
98
+ - Dependencies can be added after initialization with add_dependency()
99
+ - Token usage is tracked separately for cached vs. new tokens
100
+ - This class works with asyncio for concurrent execution
51
101
  """
52
102
  super().__init__([])
53
103
  self.answer_question_func = answer_question_func
@@ -159,8 +209,8 @@ class QuestionTaskCreator(UserList):
159
209
  @classmethod
160
210
  def example(cls):
161
211
  """Return an example instance of the class."""
162
- from edsl.questions.QuestionFreeText import QuestionFreeText
163
- from edsl.jobs.buckets.ModelBuckets import ModelBuckets
212
+ from ..questions import QuestionFreeText
213
+ from ..buckets import ModelBuckets
164
214
 
165
215
  m = ModelBuckets.infinity_bucket()
166
216
 
@@ -180,37 +230,45 @@ class QuestionTaskCreator(UserList):
180
230
  iteration=0,
181
231
  )
182
232
 
183
- async def _run_task_async(self) -> None:
184
- """Run the task asynchronously, awaiting the tasks that must be completed before this one can be run.
185
-
186
- >>> qt1 = QuestionTaskCreator.example()
187
- >>> qt2 = QuestionTaskCreator.example()
188
- >>> qt2.add_dependency(qt1)
189
-
190
- The method follows these steps:
191
- 1. Set the task_status to TaskStatus.WAITING_FOR_DEPENDENCIES, indicating that the task is waiting for its dependencies to complete.
192
- 2. Await asyncio.gather(*self, return_exceptions=True) to run all the dependent tasks concurrently.
193
-
194
- - the return_exceptions=True flag ensures that the task does not raise an exception if any of the dependencies fail.
195
-
196
- 3. If any of the dependencies raise an exception:
197
- - If it is a CancelledError, set the current task's task_status to TaskStatus.CANCELLED, and re-raise the CancelledError,
198
- terminating the execution of the current task.
199
- - If it is any other exception, set the task_status to TaskStatus.PARENT_FAILED, and raise a custom exception
200
- InterviewErrorPriorTaskCanceled with the original exception as the cause, terminating the execution of the current task.
201
- 4. If all the dependencies complete successfully without raising any exceptions, the code reaches the else block.
202
- 5. In the else block, run the focal task (self._run_focal_task(debug)).
203
-
204
- If any of the dependencies fail (raise an exception), the focal task will not run. The execution will be terminated,
205
- and an exception will be raised to indicate the failure of the dependencies.
206
-
207
- The focal task (self._run_focal_task(debug)) is only executed if all the dependencies complete successfully.
208
-
209
- Args:
210
- debug: A boolean value indicating whether to run the task in debug mode.
211
-
212
- Returns:
213
- None
233
+ async def _run_task_async(self) -> Answers:
234
+ """
235
+ Execute the task with its dependencies in an async workflow.
236
+
237
+ This method implements the core task execution logic with dependency handling.
238
+ It manages the complete lifecycle of a task:
239
+
240
+ 1. Waiting for dependencies to complete
241
+ 2. Handling dependency failures appropriately
242
+ 3. Executing the task itself when dependencies are satisfied
243
+ 4. Tracking status transitions throughout execution
244
+
245
+ The method maintains the state machine pattern by updating task_status
246
+ at each stage of execution, allowing for detailed monitoring and visualization
247
+ of task progress.
248
+
249
+ Returns:
250
+ Answers object containing the question's answer and metadata
251
+
252
+ Raises:
253
+ asyncio.CancelledError: If the task is cancelled
254
+ InterviewErrorPriorTaskCanceled: If any dependency task fails
255
+
256
+ Example:
257
+ >>> qt1 = QuestionTaskCreator.example()
258
+ >>> qt2 = QuestionTaskCreator.example()
259
+ >>> qt2.add_dependency(qt1)
260
+
261
+ Implementation details:
262
+
263
+ 1. Set status to WAITING_FOR_DEPENDENCIES and await all dependencies
264
+ - Using gather with return_exceptions=True allows collecting all results
265
+
266
+ 2. Check dependency results for exceptions:
267
+ - If CancelledError: Set status to CANCELLED and propagate the cancellation
268
+ - If other exception: Set status to PARENT_FAILED and wrap in InterviewErrorPriorTaskCanceled
269
+
270
+ 3. If all dependencies succeed, execute the focal task (_run_focal_task)
271
+ - The focal task handles its own status transitions during execution
214
272
  """
215
273
  try:
216
274
  self.task_status = TaskStatus.WAITING_FOR_DEPENDENCIES
@@ -0,0 +1,135 @@
1
+ """
2
+ This module provides the TaskCreators class, which manages all task creators for an interview.
3
+
4
+ The TaskCreators class maintains a dictionary of QuestionTaskCreator objects, each responsible
5
+ for executing a single question within an interview. It aggregates status and token usage
6
+ information across all tasks, providing a complete view of interview execution.
7
+ """
8
+
9
+ from typing import Callable, Union, List, Dict, TYPE_CHECKING
10
+ from collections import UserDict
11
+
12
+ if TYPE_CHECKING:
13
+ from ..tokens import TokenUsage
14
+ from ..tokens import InterviewTokenUsage
15
+ from ..interviews import InterviewStatusDictionary
16
+ from .task_status_log import TaskStatusLog
17
+ from .question_task_creator import QuestionTaskCreator
18
+
19
+ class TaskCreators(UserDict):
20
+ """
21
+ A collection manager for all question tasks within an interview.
22
+
23
+ The TaskCreators class maintains a dictionary of QuestionTaskCreator objects,
24
+ where each key is a question name and each value is the corresponding task creator.
25
+ This class provides methods to aggregate information across all tasks, such as
26
+ token usage and status counts, enabling a holistic view of interview execution.
27
+
28
+ In the EDSL architecture, an interview consists of multiple questions, each executed
29
+ as a separate task. The TaskCreators class helps track and manage these tasks,
30
+ maintaining their execution status and resource usage.
31
+
32
+ Key features:
33
+ - Maintains a mapping of question names to their task creators
34
+ - Aggregates token usage across all tasks
35
+ - Tracks the status of all tasks in the interview
36
+ - Provides access to status logs for visualization and analysis
37
+
38
+ This class is typically used by the Interview class to manage task execution
39
+ and track the overall status of the interview.
40
+ """
41
+
42
+ def __init__(self, *args, **kwargs):
43
+ super().__init__(*args, **kwargs)
44
+
45
+ @property
46
+ def token_usage(self) -> 'InterviewTokenUsage':
47
+ """
48
+ Calculate the total token usage across all tasks in the interview.
49
+
50
+ This property aggregates token usage statistics from all task creators,
51
+ separating cached tokens (reused from cache) from new tokens (freshly generated).
52
+ The resulting InterviewTokenUsage object provides a complete picture of token
53
+ consumption for the entire interview.
54
+
55
+ Returns:
56
+ An InterviewTokenUsage object containing:
57
+ - Cached token usage (tokens reused from cache)
58
+ - New token usage (tokens freshly generated)
59
+
60
+ Notes:
61
+ - This is useful for cost estimation and quota management
62
+ - The separation of cached vs. new tokens helps analyze caching effectiveness
63
+ - Token usage is tracked separately for prompts and completions
64
+ """
65
+ from ..tokens import TokenUsage
66
+ from ..tokens import InterviewTokenUsage
67
+
68
+ cached_tokens = TokenUsage(from_cache=True)
69
+ new_tokens = TokenUsage(from_cache=False)
70
+ for task_creator in self.values():
71
+ token_usage = task_creator.token_usage()
72
+ cached_tokens += token_usage["cached_tokens"]
73
+ new_tokens += token_usage["new_tokens"]
74
+ return InterviewTokenUsage(
75
+ new_token_usage=new_tokens, cached_token_usage=cached_tokens
76
+ )
77
+
78
+ @property
79
+ def interview_status(self) -> 'InterviewStatusDictionary':
80
+ """
81
+ Get a summary of task statuses across the entire interview.
82
+
83
+ This property counts how many tasks are in each possible status state,
84
+ providing a snapshot of the interview's current execution state. The
85
+ resulting InterviewStatusDictionary maps each TaskStatus to a count
86
+ of tasks in that state, plus a special 'number_from_cache' counter.
87
+
88
+ Returns:
89
+ An InterviewStatusDictionary with counts for each task status
90
+
91
+ Notes:
92
+ - Used for monitoring interview progress
93
+ - Helps identify bottlenecks (many tasks waiting for capacity)
94
+ - Tracks cache utilization via the 'number_from_cache' count
95
+ - Useful for status dashboards and progress reporting
96
+
97
+ Example:
98
+ >>> t = TaskCreators()
99
+ >>> status = t.interview_status
100
+ >>> 'number_from_cache' in status
101
+ True
102
+ >>> status['number_from_cache'] # Check the cache count
103
+ 0
104
+ """
105
+ from ..interviews import InterviewStatusDictionary
106
+ status_dict = InterviewStatusDictionary()
107
+ for task_creator in self.values():
108
+ status_dict[task_creator.task_status] += 1
109
+ status_dict["number_from_cache"] += task_creator.from_cache
110
+ return status_dict
111
+
112
+ def status_logs(self) -> List['TaskStatusLog']:
113
+ """
114
+ Get all task status logs for the interview.
115
+
116
+ This method collects the status logs from all task creators, providing
117
+ a complete history of status changes for every task in the interview.
118
+ The resulting list can be used for detailed analysis of task execution
119
+ patterns, timing, and visualization.
120
+
121
+ Returns:
122
+ A list of TaskStatusLog objects, one for each task in the interview
123
+
124
+ Notes:
125
+ - Used by visualization tools like task timeline charts
126
+ - Helpful for performance analysis and bottleneck identification
127
+ - Provides data for execution reports and dashboards
128
+ """
129
+ return [task_creator.status_log for task_creator in self.values()]
130
+
131
+
132
+ if __name__ == "__main__":
133
+ import doctest
134
+
135
+ doctest.testmod(optionflags=doctest.ELLIPSIS)