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,137 @@
1
+ from jinja2 import Environment, meta
2
+ from typing import Any, Set, TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from edsl.agents.PromptConstructor import PromptConstructor
6
+ from edsl.scenarios.Scenario import Scenario
7
+
8
+
9
+ class QuestionTemplateReplacementsBuilder:
10
+ def __init__(self, prompt_constructor: "PromptConstructor"):
11
+ self.prompt_constructor = prompt_constructor
12
+
13
+ def question_file_keys(self):
14
+ question_text = self.prompt_constructor.question.question_text
15
+ file_keys = self._find_file_keys(self.prompt_constructor.scenario)
16
+ return self._extract_file_keys_from_question_text(question_text, file_keys)
17
+
18
+ def scenario_file_keys(self):
19
+ return self._find_file_keys(self.prompt_constructor.scenario)
20
+
21
+ def get_jinja2_variables(template_str: str) -> Set[str]:
22
+ """
23
+ Extracts all variable names from a Jinja2 template using Jinja2's built-in parsing.
24
+
25
+ Args:
26
+ template_str (str): The Jinja2 template string
27
+
28
+ Returns:
29
+ Set[str]: A set of variable names found in the template
30
+ """
31
+ env = Environment()
32
+ ast = env.parse(template_str)
33
+ return meta.find_undeclared_variables(ast)
34
+
35
+ @staticmethod
36
+ def _find_file_keys(scenario: "Scenario") -> list:
37
+ """We need to find all the keys in the scenario that refer to FileStore objects.
38
+ These will be used to append to the prompt a list of files that are part of the scenario.
39
+
40
+ >>> from edsl import Scenario
41
+ >>> from edsl.scenarios.FileStore import FileStore
42
+ >>> import tempfile
43
+ >>> with tempfile.NamedTemporaryFile() as f:
44
+ ... _ = f.write(b"Hello, world!")
45
+ ... _ = f.seek(0)
46
+ ... fs = FileStore(f.name)
47
+ ... scenario = Scenario({"fs_file": fs, 'a': 1})
48
+ ... QuestionTemplateReplacementsBuilder._find_file_keys(scenario)
49
+ ['fs_file']
50
+ """
51
+ from edsl.scenarios.FileStore import FileStore
52
+
53
+ file_entries = []
54
+ for key, value in scenario.items():
55
+ if isinstance(value, FileStore):
56
+ file_entries.append(key)
57
+ return file_entries
58
+
59
+ @staticmethod
60
+ def _extract_file_keys_from_question_text(
61
+ question_text: str, scenario_file_keys: list
62
+ ) -> list:
63
+ """
64
+ Extracts the file keys from a question text.
65
+
66
+ >>> from edsl import Scenario
67
+ >>> from edsl.scenarios.FileStore import FileStore
68
+ >>> import tempfile
69
+ >>> with tempfile.NamedTemporaryFile() as f:
70
+ ... _ = f.write(b"Hello, world!")
71
+ ... _ = f.seek(0)
72
+ ... fs = FileStore(f.name)
73
+ ... scenario = Scenario({"fs_file": fs, 'a': 1})
74
+ ... QuestionTemplateReplacementsBuilder._extract_file_keys_from_question_text("{{ fs_file }}", ['fs_file'])
75
+ ['fs_file']
76
+ """
77
+ variables = QuestionTemplateReplacementsBuilder.get_jinja2_variables(
78
+ question_text
79
+ )
80
+ question_file_keys = []
81
+ for var in variables:
82
+ if var in scenario_file_keys:
83
+ question_file_keys.append(var)
84
+ return question_file_keys
85
+
86
+ def _scenario_replacements(self) -> dict[str, Any]:
87
+ # File references dictionary
88
+ file_refs = {key: f"<see file {key}>" for key in self.scenario_file_keys()}
89
+
90
+ # Scenario items excluding file keys
91
+ scenario_items = {
92
+ k: v
93
+ for k, v in self.prompt_constructor.scenario.items()
94
+ if k not in self.scenario_file_keys()
95
+ }
96
+ return {**file_refs, **scenario_items}
97
+
98
+ @staticmethod
99
+ def _question_data_replacements(
100
+ question: dict, question_data: dict
101
+ ) -> dict[str, Any]:
102
+ """Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources.
103
+
104
+ >>> from edsl import QuestionMultipleChoice
105
+ >>> q = QuestionMultipleChoice(question_text="Do you like school?", question_name = "q0", question_options = ["yes", "no"])
106
+ >>> QuestionTemplateReplacementsBuilder._question_data_replacements(q, q.data)
107
+ {'use_code': False, 'include_comment': True, 'question_name': 'q0', 'question_text': 'Do you like school?', 'question_options': ['yes', 'no']}
108
+
109
+ """
110
+ question_settings = {
111
+ "use_code": getattr(question, "_use_code", True),
112
+ "include_comment": getattr(question, "_include_comment", False),
113
+ }
114
+ return {**question_settings, **question_data}
115
+
116
+ def build_replacement_dict(self, question_data: dict) -> dict[str, Any]:
117
+ """Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources."""
118
+ rpl = {}
119
+ rpl["scenario"] = self._scenario_replacements()
120
+ rpl["question"] = self._question_data_replacements(
121
+ self.prompt_constructor.question, question_data
122
+ )
123
+ rpl["prior_answers"] = self.prompt_constructor.prior_answers_dict()
124
+ rpl["agent"] = {"agent": self.prompt_constructor.agent}
125
+
126
+ # Combine all dictionaries using dict.update() for clarity
127
+ replacement_dict = {}
128
+ for r in rpl.values():
129
+ replacement_dict.update(r)
130
+
131
+ return replacement_dict
132
+
133
+
134
+ if __name__ == "__main__":
135
+ import doctest
136
+
137
+ doctest.testmod()
@@ -1,42 +1,68 @@
1
+ """This module contains the descriptors used to set the attributes of the Agent class."""
2
+
1
3
  from typing import Dict
2
- from edsl.utilities.utilities import is_valid_variable_name
3
- from edsl.exceptions.agents import AgentNameError
4
+ from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
5
+
6
+
7
+ def convert_agent_name(x):
8
+ # potentially a numpy int64
9
+ import numpy as np
10
+
11
+ if isinstance(x, np.int64):
12
+ return int(x)
13
+ elif x is None:
14
+ return None
15
+ elif isinstance(x, int):
16
+ return x
17
+ else:
18
+ return str(x)
4
19
 
5
20
 
6
21
  class NameDescriptor:
7
22
  """ABC for something."""
8
23
 
9
24
  def __get__(self, instance, owner):
10
- """"""
25
+ """Return the value of the attribute."""
11
26
  return instance.__dict__[self.name]
12
27
 
13
28
  def __set__(self, instance, name: str) -> None:
14
- instance.__dict__[self.name] = name
29
+ """Set the value of the attribute."""
30
+ instance.__dict__[self.name] = convert_agent_name(name)
15
31
 
16
32
  def __set_name__(self, owner, name: str) -> None:
33
+ """Set the name of the attribute."""
17
34
  self.name = "_" + name
18
35
 
19
36
 
20
37
  class TraitsDescriptor:
21
- """ABC for something."""
38
+ """Traits descriptor."""
22
39
 
23
40
  def __get__(self, instance, owner):
24
- """"""
41
+ """Return the value of the attribute."""
25
42
  return instance.__dict__[self.name]
26
43
 
27
44
  def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
45
+ """Set the value of the attribute."""
46
+ from edsl.utilities.utilities import is_valid_variable_name
47
+
28
48
  for key, value in traits_dict.items():
29
- if not is_valid_variable_name(key):
30
- raise AgentNameError("Trait keys must be a valid variable name!")
31
49
  if key == "name":
32
50
  raise AgentNameError(
33
- """Trait keys cannot be 'name'!. Instead, use the 'name' attribute directly e.g.,
34
- >>> Agent(name="my_agent", traits={"trait1": "value1", "trait2": "value2"})
35
- """
51
+ "Trait keys cannot be 'name'. Instead, use the 'name' attribute directly e.g.,\n"
52
+ 'Agent(name="my_agent", traits={"trait1": "value1", "trait2": "value2"})'
36
53
  )
54
+
55
+ if not is_valid_variable_name(key):
56
+ raise AgentTraitKeyError(
57
+ f"""Trait keys must be valid Python identifiers (must be alphanumeric, cannot start with a number and must use underscores instead of spaces).
58
+ You passed: {key}
59
+ """
60
+ )
61
+
37
62
  instance.__dict__[self.name] = traits_dict
38
63
 
39
64
  def __set_name__(self, owner, name: str) -> None:
65
+ """Set the name of the attribute."""
40
66
  self.name = name
41
67
 
42
68
 
@@ -44,13 +70,15 @@ class CodebookDescriptor:
44
70
  """ABC for something."""
45
71
 
46
72
  def __get__(self, instance, owner):
47
- """"""
73
+ """Return the value of the attribute."""
48
74
  return instance.__dict__[self.name]
49
75
 
50
76
  def __set__(self, instance, codebook_dict: Dict[str, str]) -> None:
77
+ """Set the value of the attribute."""
51
78
  instance.__dict__[self.name] = codebook_dict
52
79
 
53
80
  def __set_name__(self, owner, name: str) -> None:
81
+ """Set the name of the attribute."""
54
82
  self.name = "_" + name
55
83
 
56
84
 
@@ -58,12 +86,14 @@ class InstructionDescriptor:
58
86
  """ABC for something."""
59
87
 
60
88
  def __get__(self, instance, owner):
61
- """"""
89
+ """Return the value of the attribute."""
62
90
  return instance.__dict__[self.name]
63
91
 
64
92
  def __set__(self, instance, instruction) -> None:
93
+ """Set the value of the attribute."""
65
94
  instance.__dict__[self.name] = instruction
66
95
  instance.set_instructions = instruction != instance.default_instruction
67
96
 
68
97
  def __set_name__(self, owner, name: str) -> None:
98
+ """Set the name of the attribute."""
69
99
  self.name = "_" + name
@@ -0,0 +1,129 @@
1
+ import enum
2
+ from typing import Dict, Optional
3
+ from collections import UserList
4
+ from edsl.prompts.Prompt import Prompt
5
+
6
+
7
+ class PromptComponent(enum.Enum):
8
+ AGENT_INSTRUCTIONS = "agent_instructions"
9
+ AGENT_PERSONA = "agent_persona"
10
+ QUESTION_INSTRUCTIONS = "question_instructions"
11
+ PRIOR_QUESTION_MEMORY = "prior_question_memory"
12
+
13
+
14
+ class PromptList(UserList):
15
+ separator = Prompt("")
16
+
17
+ def reduce(self):
18
+ """Reduce the list of prompts to a single prompt.
19
+
20
+ >>> p = PromptList([Prompt("You are a happy-go lucky agent."), Prompt("You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}")])
21
+ >>> p.reduce()
22
+ Prompt(text=\"""You are a happy-go lucky agent.You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}\""")
23
+
24
+ """
25
+ p = self[0]
26
+ for prompt in self[1:]:
27
+ if len(prompt) > 0:
28
+ p = p + self.separator + prompt
29
+ return p
30
+
31
+
32
+ class PromptPlan:
33
+ """A plan for constructing prompts for the LLM call.
34
+ Every prompt plan has a user prompt order and a system prompt order.
35
+ It must contain each of the values in the PromptComponent enum.
36
+
37
+
38
+ >>> p = PromptPlan(user_prompt_order=(PromptComponent.AGENT_INSTRUCTIONS, PromptComponent.AGENT_PERSONA),system_prompt_order=(PromptComponent.QUESTION_INSTRUCTIONS, PromptComponent.PRIOR_QUESTION_MEMORY))
39
+ >>> p._is_valid_plan()
40
+ True
41
+
42
+ >>> p.arrange_components(agent_instructions=1, agent_persona=2, question_instructions=3, prior_question_memory=4)
43
+ {'user_prompt': ..., 'system_prompt': ...}
44
+
45
+ >>> p = PromptPlan(user_prompt_order=("agent_instructions", ), system_prompt_order=("question_instructions", "prior_question_memory"))
46
+ Traceback (most recent call last):
47
+ ...
48
+ ValueError: Invalid plan: must contain each value of PromptComponent exactly once.
49
+
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ user_prompt_order: Optional[tuple] = None,
55
+ system_prompt_order: Optional[tuple] = None,
56
+ ):
57
+ """Initialize the PromptPlan."""
58
+
59
+ if user_prompt_order is None:
60
+ user_prompt_order = (
61
+ PromptComponent.QUESTION_INSTRUCTIONS,
62
+ PromptComponent.PRIOR_QUESTION_MEMORY,
63
+ )
64
+ if system_prompt_order is None:
65
+ system_prompt_order = (
66
+ PromptComponent.AGENT_INSTRUCTIONS,
67
+ PromptComponent.AGENT_PERSONA,
68
+ )
69
+
70
+ # very commmon way to screw this up given how python treats single strings as iterables
71
+ if isinstance(user_prompt_order, str):
72
+ user_prompt_order = (user_prompt_order,)
73
+
74
+ if isinstance(system_prompt_order, str):
75
+ system_prompt_order = (system_prompt_order,)
76
+
77
+ if not isinstance(user_prompt_order, tuple):
78
+ raise TypeError(
79
+ f"Expected a tuple, but got {type(user_prompt_order).__name__}"
80
+ )
81
+
82
+ if not isinstance(system_prompt_order, tuple):
83
+ raise TypeError(
84
+ f"Expected a tuple, but got {type(system_prompt_order).__name__}"
85
+ )
86
+
87
+ self.user_prompt_order = self._convert_to_enum(user_prompt_order)
88
+ self.system_prompt_order = self._convert_to_enum(system_prompt_order)
89
+ if not self._is_valid_plan():
90
+ raise ValueError(
91
+ "Invalid plan: must contain each value of PromptComponent exactly once."
92
+ )
93
+
94
+ def _convert_to_enum(self, prompt_order: tuple):
95
+ """Convert string names to PromptComponent enum values."""
96
+ return tuple(
97
+ PromptComponent(component) if isinstance(component, str) else component
98
+ for component in prompt_order
99
+ )
100
+
101
+ def _is_valid_plan(self):
102
+ """Check if the plan is valid."""
103
+ combined = self.user_prompt_order + self.system_prompt_order
104
+ return set(combined) == set(PromptComponent)
105
+
106
+ def arrange_components(self, **kwargs) -> Dict[PromptComponent, Prompt]:
107
+ """Arrange the components in the order specified by the plan."""
108
+ # check is valid components passed
109
+ component_strings = set([pc.value for pc in PromptComponent])
110
+ if not set(kwargs.keys()) == component_strings:
111
+ raise ValueError(
112
+ f"Invalid components passed: {set(kwargs.keys())} but expected {PromptComponent}"
113
+ )
114
+
115
+ user_prompt = PromptList(
116
+ [kwargs[component.value] for component in self.user_prompt_order]
117
+ )
118
+ system_prompt = PromptList(
119
+ [kwargs[component.value] for component in self.system_prompt_order]
120
+ )
121
+ return {"user_prompt": user_prompt, "system_prompt": system_prompt}
122
+
123
+ def get_prompts(self, **kwargs) -> Dict[str, Prompt]:
124
+ """Get both prompts for the LLM call."""
125
+ prompts = self.arrange_components(**kwargs)
126
+ return {
127
+ "user_prompt": prompts["user_prompt"].reduce(),
128
+ "system_prompt": prompts["system_prompt"].reduce(),
129
+ }
@@ -0,0 +1,172 @@
1
+ from jinja2 import Environment, meta
2
+ from typing import List, Optional, Union
3
+
4
+
5
+ class QuestionOptionProcessor:
6
+ """
7
+ Class that manages the processing of question options.
8
+ These can be provided directly, as a template string, or fetched from prior answers or the scenario.
9
+ """
10
+
11
+ def __init__(self, prompt_constructor):
12
+ self.prompt_constructor = prompt_constructor
13
+
14
+ @staticmethod
15
+ def _get_default_options() -> list:
16
+ """Return default placeholder options."""
17
+ return [f"<< Option {i} - Placeholder >>" for i in range(1, 4)]
18
+
19
+ @staticmethod
20
+ def _parse_template_variable(template_str: str) -> str:
21
+ """
22
+ Extract the variable name from a template string.
23
+
24
+ Args:
25
+ template_str (str): Jinja template string
26
+
27
+ Returns:
28
+ str: Name of the first undefined variable in the template
29
+
30
+ >>> QuestionOptionProcessor._parse_template_variable("Here are some {{ options }}")
31
+ 'options'
32
+ >>> QuestionOptionProcessor._parse_template_variable("Here are some {{ options }} and {{ other }}")
33
+ Traceback (most recent call last):
34
+ ...
35
+ ValueError: Multiple variables found in template string
36
+ >>> QuestionOptionProcessor._parse_template_variable("Here are some")
37
+ Traceback (most recent call last):
38
+ ...
39
+ ValueError: No variables found in template string
40
+ """
41
+ env = Environment()
42
+ parsed_content = env.parse(template_str)
43
+ undeclared_variables = list(meta.find_undeclared_variables(parsed_content))
44
+ if not undeclared_variables:
45
+ raise ValueError("No variables found in template string")
46
+ if len(undeclared_variables) > 1:
47
+ raise ValueError("Multiple variables found in template string")
48
+ return undeclared_variables[0]
49
+
50
+ @staticmethod
51
+ def _get_options_from_scenario(
52
+ scenario: dict, option_key: str
53
+ ) -> Union[list, None]:
54
+ """
55
+ Try to get options from scenario data.
56
+
57
+ >>> from edsl import Scenario
58
+ >>> scenario = Scenario({"options": ["Option 1", "Option 2"]})
59
+ >>> QuestionOptionProcessor._get_options_from_scenario(scenario, "options")
60
+ ['Option 1', 'Option 2']
61
+
62
+
63
+ Returns:
64
+ list | None: List of options if found in scenario, None otherwise
65
+ """
66
+ scenario_options = scenario.get(option_key)
67
+ return scenario_options if isinstance(scenario_options, list) else None
68
+
69
+ @staticmethod
70
+ def _get_options_from_prior_answers(
71
+ prior_answers: dict, option_key: str
72
+ ) -> Union[list, None]:
73
+ """
74
+ Try to get options from prior answers.
75
+
76
+ prior_answers (dict): Dictionary of prior answers
77
+ option_key (str): Key to look up in prior answers
78
+
79
+ >>> from edsl import QuestionList as Q
80
+ >>> q = Q.example()
81
+ >>> q.answer = ["Option 1", "Option 2"]
82
+ >>> prior_answers = {"options": q}
83
+ >>> QuestionOptionProcessor._get_options_from_prior_answers(prior_answers, "options")
84
+ ['Option 1', 'Option 2']
85
+ >>> QuestionOptionProcessor._get_options_from_prior_answers(prior_answers, "wrong_key") is None
86
+ True
87
+
88
+ Returns:
89
+ list | None: List of options if found in prior answers, None otherwise
90
+ """
91
+ prior_answer = prior_answers.get(option_key)
92
+ if prior_answer and hasattr(prior_answer, "answer"):
93
+ if isinstance(prior_answer.answer, list):
94
+ return prior_answer.answer
95
+ return None
96
+
97
+ def get_question_options(self, question_data: dict) -> list:
98
+ """
99
+ Extract and process question options from question data.
100
+
101
+ Args:
102
+ question_data (dict): Dictionary containing question configuration
103
+
104
+ Returns:
105
+ list: List of question options. Returns default placeholders if no valid options found.
106
+
107
+ >>> class MockPromptConstructor:
108
+ ... pass
109
+ >>> mpc = MockPromptConstructor()
110
+ >>> from edsl import Scenario
111
+ >>> mpc.scenario = Scenario({"options": ["Option 1", "Option 2"]})
112
+ >>> processor = QuestionOptionProcessor(mpc)
113
+
114
+ The basic case where options are directly provided:
115
+
116
+ >>> question_data = {"question_options": ["Option 1", "Option 2"]}
117
+ >>> processor.get_question_options(question_data)
118
+ ['Option 1', 'Option 2']
119
+
120
+ The case where options are provided as a template string:
121
+
122
+ >>> question_data = {"question_options": "{{ options }}"}
123
+ >>> processor.get_question_options(question_data)
124
+ ['Option 1', 'Option 2']
125
+
126
+ The case where there is a templace string but it's in the prior answers:
127
+
128
+ >>> class MockQuestion:
129
+ ... pass
130
+ >>> q0 = MockQuestion()
131
+ >>> q0.answer = ["Option 1", "Option 2"]
132
+ >>> mpc.prior_answers_dict = lambda: {'q0': q0}
133
+ >>> processor = QuestionOptionProcessor(mpc)
134
+ >>> question_data = {"question_options": "{{ q0 }}"}
135
+ >>> processor.get_question_options(question_data)
136
+ ['Option 1', 'Option 2']
137
+
138
+ The case we're no options are found:
139
+ >>> processor.get_question_options({"question_options": "{{ poop }}"})
140
+ ['<< Option 1 - Placeholder >>', '<< Option 2 - Placeholder >>', '<< Option 3 - Placeholder >>']
141
+
142
+ """
143
+ options_entry = question_data.get("question_options")
144
+
145
+ # If not a template string, return as is or default
146
+ if not isinstance(options_entry, str):
147
+ return options_entry if options_entry else self._get_default_options()
148
+
149
+ # Parse template to get variable name
150
+ option_key = self._parse_template_variable(options_entry)
151
+
152
+ # Try getting options from scenario
153
+ scenario_options = self._get_options_from_scenario(
154
+ self.prompt_constructor.scenario, option_key
155
+ )
156
+ if scenario_options:
157
+ return scenario_options
158
+
159
+ # Try getting options from prior answers
160
+ prior_answer_options = self._get_options_from_prior_answers(
161
+ self.prompt_constructor.prior_answers_dict(), option_key
162
+ )
163
+ if prior_answer_options:
164
+ return prior_answer_options
165
+
166
+ return self._get_default_options()
167
+
168
+
169
+ if __name__ == "__main__":
170
+ import doctest
171
+
172
+ doctest.testmod()
edsl/auto/AutoStudy.py ADDED
@@ -0,0 +1,130 @@
1
+ from typing import Optional, TYPE_CHECKING
2
+
3
+ from edsl import Model
4
+ from edsl.auto.StageQuestions import StageQuestions
5
+ from edsl.auto.StagePersona import StagePersona
6
+ from edsl.auto.StagePersonaDimensions import StagePersonaDimensions
7
+ from edsl.auto.StagePersonaDimensionValues import StagePersonaDimensionValues
8
+ from edsl.auto.StagePersonaDimensionValueRanges import (
9
+ StagePersonaDimensionValueRanges,
10
+ )
11
+ from edsl.auto.StageLabelQuestions import StageLabelQuestions
12
+ from edsl.auto.StageGenerateSurvey import StageGenerateSurvey
13
+
14
+ from edsl.auto.utilities import agent_generator, create_agents, gen_pipeline
15
+
16
+ if TYPE_CHECKING:
17
+ from edsl.surveys.Survey import Survey
18
+ from edsl.agents.AgentList import AgentList
19
+
20
+
21
+ class AutoStudy:
22
+ def __init__(
23
+ self,
24
+ overall_question: str,
25
+ population: str,
26
+ model: Optional["Model"] = None,
27
+ survey: Optional["Survey"] = None,
28
+ agent_list: Optional["AgentList"] = None,
29
+ default_num_agents: int = 11,
30
+ ):
31
+ """AutoStudy class for generating surveys and agents."""
32
+
33
+ self.overall_question = overall_question
34
+ self.population = population
35
+ self._survey = survey
36
+ self._agent_list = agent_list
37
+ self._agent_list_generator = None
38
+ self._persona_mapping = None
39
+ self._results = None
40
+ self.default_num_agents = default_num_agents
41
+ self.model = model or Model()
42
+
43
+ def to_dict(self):
44
+ return {
45
+ "overall_question": self.overall_question,
46
+ "population": self.population,
47
+ "survey": self.survey.to_dict(),
48
+ "persona_mapping": self.persona_mapping.to_dict(),
49
+ "results": self.results.to_dict(),
50
+ }
51
+
52
+ @property
53
+ def survey(self):
54
+ if self._survey is None:
55
+ self._survey = self._create_survey()
56
+ return self._survey
57
+
58
+ @property
59
+ def persona_mapping(self):
60
+ if self._persona_mapping is None:
61
+ self._persona_mapping = self._create_persona_mapping()
62
+ return self._persona_mapping
63
+
64
+ @property
65
+ def agent_list_generator(self):
66
+ if self._agent_list_generator is None:
67
+ self._agent_list_generator = self._create_agent_list_generator()
68
+ return self._agent_list_generator
69
+
70
+ @property
71
+ def results(self):
72
+ if self._results is None:
73
+ self._results = self._create_results()
74
+ return self._results
75
+
76
+ def _create_survey(self):
77
+ survey_pipline_stages = [
78
+ StageQuestions,
79
+ StageLabelQuestions,
80
+ StageGenerateSurvey,
81
+ ]
82
+ survey_pipeline = gen_pipeline(survey_pipline_stages)
83
+ return survey_pipeline.process(
84
+ data=survey_pipeline.input(
85
+ overall_question=self.overall_question, population=self.population
86
+ )
87
+ ).survey
88
+
89
+ def _create_persona_mapping(self):
90
+ persona_pipeline_stages = [
91
+ StageQuestions,
92
+ StagePersona,
93
+ StagePersonaDimensions,
94
+ StagePersonaDimensionValues,
95
+ StagePersonaDimensionValueRanges,
96
+ ]
97
+
98
+ persona_pipeline = gen_pipeline(persona_pipeline_stages)
99
+ sample_agent_results = persona_pipeline.process(
100
+ persona_pipeline.input(
101
+ overall_question=overall_question, population=self.population
102
+ )
103
+ )
104
+ return sample_agent_results
105
+
106
+ def _create_agent_list_generator(self):
107
+ return agent_generator(
108
+ persona=self.persona_mapping.persona,
109
+ dimension_dict=self.persona_mapping.mapping,
110
+ )
111
+
112
+ def agent_list(self, num_agents):
113
+ return create_agents(
114
+ agent_generator=self.agent_list_generator,
115
+ survey=self.survey,
116
+ num_agents=num_agents,
117
+ )
118
+
119
+ def _create_results(self, num_agents=None):
120
+ if num_agents is None:
121
+ num_agents = self.default_num_agents
122
+ agent_list = self.agent_list(num_agents)
123
+ return self.survey.by(agent_list).by(self.model).run()
124
+
125
+
126
+ if __name__ == "__main__":
127
+ overall_question = "I have an open source Python library for working with LLMs. What are some ways we can market this to others?"
128
+ auto_study = AutoStudy(overall_question, population="US Adults")
129
+
130
+ results = auto_study.results