edsl 0.1.47__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 (314) 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 +303 -67
  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/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
  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} +3 -7
  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} +313 -167
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -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 +4 -9
  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} +365 -220
  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/{FileStore.py → file_store.py} +275 -189
  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} +294 -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 +18 -19
  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.47.dist-info → edsl-0.1.48.dist-info}/METADATA +1 -1
  230. edsl-0.1.48.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -493
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/coop/PriceFetcher.py +0 -54
  236. edsl/data/Cache.py +0 -582
  237. edsl/data/CacheEntry.py +0 -238
  238. edsl/data/SQLiteDict.py +0 -292
  239. edsl/data/__init__.py +0 -5
  240. edsl/data/orm.py +0 -10
  241. edsl/exceptions/cache.py +0 -5
  242. edsl/exceptions/coop.py +0 -14
  243. edsl/exceptions/data.py +0 -14
  244. edsl/exceptions/scenarios.py +0 -29
  245. edsl/jobs/Answers.py +0 -43
  246. edsl/jobs/JobsPrompts.py +0 -354
  247. edsl/jobs/buckets/BucketCollection.py +0 -134
  248. edsl/jobs/buckets/ModelBuckets.py +0 -65
  249. edsl/jobs/buckets/TokenBucket.py +0 -283
  250. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  251. edsl/jobs/interviews/Interview.py +0 -395
  252. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  253. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  254. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  255. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  256. edsl/jobs/tasks/TaskCreators.py +0 -64
  257. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  258. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  259. edsl/language_models/LanguageModel.py +0 -635
  260. edsl/language_models/ServiceDataSources.py +0 -0
  261. edsl/language_models/key_management/KeyLookup.py +0 -63
  262. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  263. edsl/language_models/key_management/models.py +0 -137
  264. edsl/questions/QuestionBase.py +0 -544
  265. edsl/questions/QuestionFreeText.py +0 -130
  266. edsl/questions/derived/QuestionLikertFive.py +0 -76
  267. edsl/results/ResultsExportMixin.py +0 -45
  268. edsl/results/TextEditor.py +0 -50
  269. edsl/results/results_fetch_mixin.py +0 -33
  270. edsl/results/results_tools_mixin.py +0 -98
  271. edsl/scenarios/DocumentChunker.py +0 -104
  272. edsl/scenarios/Scenario.py +0 -548
  273. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  274. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  275. edsl/scenarios/handlers/latex.py +0 -5
  276. edsl/shared.py +0 -1
  277. edsl/surveys/Survey.py +0 -1301
  278. edsl/surveys/SurveyQualtricsImport.py +0 -284
  279. edsl/surveys/SurveyToApp.py +0 -141
  280. edsl/surveys/instructions/__init__.py +0 -0
  281. edsl/tools/__init__.py +0 -1
  282. edsl/tools/clusters.py +0 -192
  283. edsl/tools/embeddings.py +0 -27
  284. edsl/tools/embeddings_plotting.py +0 -118
  285. edsl/tools/plotting.py +0 -112
  286. edsl/tools/summarize.py +0 -18
  287. edsl/utilities/data/Registry.py +0 -6
  288. edsl/utilities/data/__init__.py +0 -1
  289. edsl/utilities/data/scooter_results.json +0 -1
  290. edsl-0.1.47.dist-info/RECORD +0 -354
  291. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  292. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  293. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  294. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  295. /edsl/{results → dataset/display}/table_display.css +0 -0
  296. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  297. /edsl/{results → dataset}/tree_explore.py +0 -0
  298. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  299. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  300. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  301. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  302. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  303. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  304. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  305. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  306. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  307. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  308. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  309. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  310. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  311. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  312. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  313. {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -4,9 +4,8 @@ import itertools
4
4
  from typing import Optional, List, Callable, Type, TYPE_CHECKING, Union
5
5
 
6
6
  if TYPE_CHECKING:
7
- from edsl.questions.QuestionBase import QuestionBase
8
- from edsl.scenarios.ScenarioList import ScenarioList
9
-
7
+ from .question_base import QuestionBase
8
+ from ..scenarios import ScenarioList
10
9
 
11
10
  class QuestionBaseGenMixin:
12
11
  """Mixin for QuestionBase.
@@ -30,7 +29,7 @@ class QuestionBaseGenMixin:
30
29
  def option_permutations(self) -> list[QuestionBase]:
31
30
  """Return a list of questions with all possible permutations of the options.
32
31
 
33
- >>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice as Q
32
+ >>> from edsl.questions import QuestionMultipleChoice as Q
34
33
  >>> len(Q.example().option_permutations())
35
34
  24
36
35
  """
@@ -53,7 +52,7 @@ class QuestionBaseGenMixin:
53
52
 
54
53
  If the question has no options, returns a copy of the original question.
55
54
 
56
- >>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice as Q
55
+ >>> from edsl.questions import QuestionMultipleChoice as Q
57
56
  >>> q = Q.example()
58
57
  >>> drawn = q.draw()
59
58
  >>> len(drawn.question_options) == len(q.question_options)
@@ -78,8 +77,8 @@ class QuestionBaseGenMixin:
78
77
 
79
78
  :param scenario_list: The list of scenarios to loop through.
80
79
 
81
- >>> from edsl.questions.QuestionFreeText import QuestionFreeText
82
- >>> from edsl.scenarios.ScenarioList import ScenarioList
80
+ >>> from edsl.questions import QuestionFreeText
81
+ >>> from edsl.scenarios import ScenarioList
83
82
  >>> q = QuestionFreeText(question_text = "What are your thoughts on: {{ subject}}?", question_name = "base_{{subject}}")
84
83
  >>> len(q.loop(ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])))
85
84
  3
@@ -100,31 +99,31 @@ class QuestionBaseGenMixin:
100
99
  Raises:
101
100
  MaxTemplateNestingExceeded: If template nesting exceeds MAX_NESTING levels
102
101
 
103
- >>> from edsl.questions.QuestionFreeText import QuestionFreeText
102
+ >>> from edsl.questions import QuestionFreeText
104
103
  >>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite {{ thing }}?")
105
104
  >>> q.render({"thing": "color"})
106
105
  Question('free_text', question_name = \"""color\""", question_text = \"""What is your favorite color?\""")
107
106
 
108
- >>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
107
+ >>> from edsl.questions import QuestionMultipleChoice
109
108
  >>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
110
- >>> from edsl.scenarios.Scenario import Scenario
109
+ >>> from edsl.scenarios import Scenario
111
110
  >>> q.render(Scenario({"thing": "color"})).data
112
111
  {'question_name': 'color', 'question_text': 'What is your favorite color?', 'question_options': ['red', 'blue', 'green']}
113
112
 
114
- >>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
113
+ >>> from edsl.questions import QuestionMultipleChoice
115
114
  >>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
116
115
  >>> q.render({"thing": 1}).data
117
116
  {'question_name': 'color', 'question_text': 'What is your favorite 1?', 'question_options': ['red', 'blue', 'green']}
118
117
 
119
118
 
120
- >>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
121
- >>> from edsl.scenarios.Scenario import Scenario
119
+ >>> from edsl.questions import QuestionMultipleChoice
120
+ >>> from edsl.scenarios import Scenario
122
121
  >>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
123
122
  >>> q.render(Scenario({"thing": "color of {{ object }}", "object":"water"})).data
124
123
  {'question_name': 'color', 'question_text': 'What is your favorite color of water?', 'question_options': ['red', 'blue', 'green']}
125
124
 
126
125
 
127
- >>> from edsl.questions.QuestionFreeText import QuestionFreeText
126
+ >>> from edsl.questions import QuestionFreeText
128
127
  >>> q = QuestionFreeText(question_name = "infinite", question_text = "This has {{ a }}")
129
128
  >>> q.render({"a": "{{ b }}", "b": "{{ a }}"}) # doctest: +IGNORE_EXCEPTION_DETAIL
130
129
  Traceback (most recent call last):
@@ -132,7 +131,7 @@ class QuestionBaseGenMixin:
132
131
  edsl.questions.question_base_gen_mixin.QuestionBaseGenMixin.MaxTemplateNestingExceeded:...
133
132
  """
134
133
  from jinja2 import Environment, meta
135
- from edsl.scenarios.Scenario import Scenario
134
+ from edsl.scenarios import Scenario
136
135
 
137
136
  MAX_NESTING = 10 # Maximum allowed nesting levels
138
137
 
@@ -187,7 +186,7 @@ class QuestionBaseGenMixin:
187
186
  def apply_function(
188
187
  self, func: Callable, exclude_components: Optional[List[str]] = None
189
188
  ) -> QuestionBase:
190
- from edsl.questions.QuestionBase import QuestionBase
189
+ from .question_base import QuestionBase
191
190
  d = self._apply_function_dict(func, exclude_components)
192
191
  return QuestionBase.from_dict(d)
193
192
 
@@ -1,8 +1,8 @@
1
1
  from importlib import resources
2
2
  from typing import Optional
3
- from edsl.exceptions.questions import QuestionAnswerValidationError
4
3
  from functools import lru_cache
5
4
 
5
+ from .exceptions import QuestionAnswerValidationError
6
6
 
7
7
  class TemplateManager:
8
8
  _instance = None
@@ -277,7 +277,7 @@ class QuestionBasePromptsMixin:
277
277
 
278
278
  :param model: The language model to use.
279
279
  """
280
- from edsl.prompts.Prompt import Prompt
280
+ from ..prompts import Prompt
281
281
 
282
282
  if model in self.model_instructions:
283
283
  return Prompt(text=self.model_instructions[model])
@@ -3,10 +3,9 @@ from typing import Any, Optional, Union, List
3
3
 
4
4
  from pydantic import Field, BaseModel, validator
5
5
 
6
- from edsl.questions.QuestionBase import QuestionBase
7
- from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
8
- from edsl.questions.response_validator_abc import ResponseValidatorABC
9
-
6
+ from .question_base import QuestionBase
7
+ from .descriptors import IntegerDescriptor, QuestionOptionsDescriptor
8
+ from .response_validator_abc import ResponseValidatorABC
10
9
 
11
10
  class BudgetResponseValidator(ResponseValidatorABC):
12
11
  valid_examples = []
@@ -1,25 +1,27 @@
1
1
  from __future__ import annotations
2
2
  import random
3
- from typing import Any, Optional, Union
3
+ from typing import Any, Optional, Union, TYPE_CHECKING
4
4
 
5
5
  from jinja2 import Template
6
+ from pydantic import field_validator
7
+ from pydantic import BaseModel, Field, conlist
8
+ from typing import List, Literal, Optional, Annotated
6
9
 
7
- from edsl.questions.QuestionBase import QuestionBase
8
- from edsl.questions.descriptors import (
10
+ from .exceptions import QuestionAnswerValidationError
11
+ from ..scenarios import Scenario
12
+
13
+ from .question_base import QuestionBase
14
+ from .descriptors import (
9
15
  IntegerDescriptor,
10
16
  QuestionOptionsDescriptor,
11
17
  )
18
+ from .decorators import inject_exception
19
+ from .response_validator_abc import ResponseValidatorABC
12
20
 
13
- from edsl.questions.decorators import inject_exception
14
-
15
- from pydantic import field_validator
16
- from edsl.questions.response_validator_abc import ResponseValidatorABC
17
- from edsl.questions.data_structures import BaseResponse
18
-
19
- from edsl.exceptions.questions import QuestionAnswerValidationError
20
-
21
- from pydantic import BaseModel, Field, conlist
22
- from typing import List, Literal, Optional, Annotated
21
+ if TYPE_CHECKING:
22
+ from .data_structures import (
23
+ BaseResponse,
24
+ )
23
25
 
24
26
 
25
27
  def create_checkbox_response_model(
@@ -164,7 +166,7 @@ class CheckBoxResponseValidator(ResponseValidatorABC):
164
166
  print(f"Proposed solution {proposed_data} is invalid. Error: {e}")
165
167
  return response
166
168
 
167
- def custom_validate(self, response) -> BaseResponse:
169
+ def custom_validate(self, response) -> 'BaseResponse':
168
170
  if response.answer is None:
169
171
  raise QuestionAnswerValidationError("Answer is missing.")
170
172
  return response.dict()
@@ -241,8 +243,6 @@ class QuestionCheckBox(QuestionBase):
241
243
  For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
242
244
  The LLM will respond with [0,1] and this code will translate it to ["a","b"].
243
245
  """
244
- from edsl.scenarios.Scenario import Scenario
245
-
246
246
  scenario = scenario or Scenario()
247
247
  translated_options = [
248
248
  Template(str(option)).render(scenario) for option in self.question_options
@@ -4,16 +4,16 @@ from pydantic import BaseModel, Field, field_validator
4
4
  from jinja2 import Environment, FileSystemLoader, TemplateNotFound
5
5
  from pathlib import Path
6
6
 
7
- from edsl.questions.QuestionBase import QuestionBase
8
- from edsl.questions.descriptors import (
7
+ from .question_base import QuestionBase
8
+ from .descriptors import (
9
9
  AnswerKeysDescriptor,
10
10
  ValueTypesDescriptor,
11
11
  ValueDescriptionsDescriptor,
12
12
  QuestionTextDescriptor,
13
13
  )
14
- from edsl.questions.response_validator_abc import ResponseValidatorABC
15
- from edsl.exceptions.questions import QuestionCreationValidationError
16
- from edsl.questions.decorators import inject_exception
14
+ from .response_validator_abc import ResponseValidatorABC
15
+ from .exceptions import QuestionCreationValidationError
16
+ from .decorators import inject_exception
17
17
 
18
18
 
19
19
  class DictResponseValidator(ResponseValidatorABC):
@@ -49,6 +49,40 @@ class DictResponseValidator(ResponseValidatorABC):
49
49
 
50
50
 
51
51
  class QuestionDict(QuestionBase):
52
+ """ A QuestionDict allows you to create questions that expect dictionary responses with specific keys and value types.
53
+
54
+ Documenation: https://docs.expectedparrot.com/en/latest/questions.html#questiondict
55
+
56
+ Parameters
57
+ ----------
58
+ question_name : str
59
+ Unique identifier for the question
60
+ question_text : str
61
+ The actual question text presented to users
62
+ answer_keys : List[str]
63
+ Keys that must be provided in the answer dictionary
64
+ value_types : Optional[List[Union[str, type]]]
65
+ Expected data types for each answer key
66
+ value_descriptions : Optional[List[str]]
67
+ Human-readable descriptions for each answer key
68
+ include_comment : bool
69
+ Whether to allow additional comments with the answer
70
+ question_presentation : Optional[str]
71
+ Alternative way to present the question
72
+ answering_instructions : Optional[str]
73
+ Additional instructions for answering
74
+ permissive : bool
75
+ If True, allows additional keys not specified in answer_keys
76
+
77
+ Examples
78
+ --------
79
+ >>> q = QuestionDict(
80
+ ... question_name="tweet",
81
+ ... question_text="Draft a tweet.",
82
+ ... answer_keys=["text", "characters"],
83
+ ... value_descriptions=["The text of the tweet", "The number of characters in the tweet"]
84
+ ... )
85
+ """
52
86
  question_type = "dict"
53
87
  question_text: str = QuestionTextDescriptor()
54
88
  answer_keys: List[str] = AnswerKeysDescriptor()
@@ -1,18 +1,18 @@
1
1
  from __future__ import annotations
2
2
  import json
3
3
  import re
4
-
4
+ from typing import Dict, Any
5
5
  from typing import Any, Optional, Dict
6
- from edsl.questions.QuestionBase import QuestionBase
7
- from edsl.questions.descriptors import AnswerTemplateDescriptor
8
-
9
- from edsl.questions.response_validator_abc import ResponseValidatorABC
10
- from edsl.questions.data_structures import BaseResponse
11
- from edsl.questions.decorators import inject_exception
12
6
 
13
- from typing import Dict, Any
14
7
  from pydantic import create_model, Field
15
8
 
9
+ from .question_base import QuestionBase
10
+ from .descriptors import AnswerTemplateDescriptor
11
+
12
+ from .response_validator_abc import ResponseValidatorABC
13
+ from .data_structures import BaseResponse
14
+ from .decorators import inject_exception
15
+
16
16
 
17
17
  def extract_json(text, expected_keys, verbose=False):
18
18
  # Escape special regex characters in keys
@@ -156,7 +156,7 @@ class QuestionExtract(QuestionBase):
156
156
 
157
157
  def main():
158
158
  """Administer a question and validate the answer."""
159
- from edsl.questions.QuestionExtract import QuestionExtract
159
+ from edsl.questions import QuestionExtract
160
160
 
161
161
  q = QuestionExtract.example()
162
162
  q.question_text
@@ -0,0 +1,282 @@
1
+ from __future__ import annotations
2
+ from typing import Any, Optional
3
+ from typing import Optional, Any, List
4
+
5
+ from uuid import uuid4
6
+
7
+ from pydantic import field_validator, model_validator, BaseModel
8
+
9
+ from ..prompts import Prompt
10
+
11
+ from .exceptions import QuestionAnswerValidationError
12
+ from .question_base import QuestionBase
13
+ from .response_validator_abc import ResponseValidatorABC
14
+ from .decorators import inject_exception
15
+
16
+
17
+ class FreeTextResponse(BaseModel):
18
+ """
19
+ Pydantic model for validating free text responses.
20
+
21
+ This model defines the structure and validation rules for responses to
22
+ free text questions. It ensures that responses contain a valid text string
23
+ and that the answer and generated_tokens fields match when both are present.
24
+
25
+ Attributes:
26
+ answer: The text response string.
27
+ generated_tokens: Optional raw LLM output for token tracking.
28
+ """
29
+
30
+ answer: str
31
+ generated_tokens: Optional[str] = None
32
+
33
+ @model_validator(mode='after')
34
+ def validate_tokens_match_answer(self):
35
+ """
36
+ Validate that the answer matches the generated tokens if provided.
37
+
38
+ This validator ensures consistency between the answer and generated_tokens
39
+ fields when both are present. They must match exactly (after stripping
40
+ whitespace) to ensure token tracking accuracy.
41
+
42
+ Returns:
43
+ The validated model instance.
44
+
45
+ Raises:
46
+ ValueError: If the answer and generated_tokens don't match exactly.
47
+ """
48
+ if self.generated_tokens is not None: # If generated_tokens exists
49
+ # Ensure exact string equality
50
+ if self.answer.strip() != self.generated_tokens.strip(): # They MUST match exactly
51
+ raise ValueError(
52
+ f"answer '{self.answer}' must exactly match generated_tokens '{self.generated_tokens}'. "
53
+ f"Type of answer: {type(self.answer)}, Type of tokens: {type(self.generated_tokens)}"
54
+ )
55
+ return self
56
+
57
+
58
+ class FreeTextResponseValidator(ResponseValidatorABC):
59
+ """
60
+ Validator for free text question responses.
61
+
62
+ This class implements the validation and fixing logic for free text responses.
63
+ It ensures that responses contain a valid text string and provides methods
64
+ to fix common issues in responses.
65
+
66
+ Attributes:
67
+ required_params: List of required parameters for validation.
68
+ valid_examples: Examples of valid responses for testing.
69
+ invalid_examples: Examples of invalid responses for testing.
70
+ """
71
+ required_params = []
72
+ valid_examples = [({"answer": "This is great"}, {})]
73
+ invalid_examples = [
74
+ (
75
+ {"answer": None},
76
+ {},
77
+ "Answer code must not be missing.",
78
+ ),
79
+ ]
80
+
81
+ def fix(self, response: dict, verbose: bool = False) -> dict:
82
+ """
83
+ Fix common issues in free text responses.
84
+
85
+ This method attempts to fix invalid responses by ensuring the answer
86
+ field contains a valid string and is consistent with the generated_tokens
87
+ field if present.
88
+
89
+ Args:
90
+ response: The response dictionary to fix.
91
+ verbose: If True, print information about the fixing process.
92
+
93
+ Returns:
94
+ A fixed version of the response dictionary.
95
+
96
+ Notes:
97
+ - For free text responses, the answer is always synchronized with generated_tokens
98
+ - Both fields are converted to strings to ensure type consistency
99
+ """
100
+ if response.get("generated_tokens") != response.get("answer"):
101
+ return {
102
+ "answer": str(response.get("generated_tokens")),
103
+ "generated_tokens": str(response.get("generated_tokens")),
104
+ }
105
+ else:
106
+ return {
107
+ "answer": str(response.get("generated_tokens")),
108
+ "generated_tokens": str(response.get("generated_tokens")),
109
+ }
110
+
111
+
112
+ class QuestionFreeText(QuestionBase):
113
+ """
114
+ A question that allows an agent to respond with free-form text.
115
+
116
+ QuestionFreeText is one of the simplest and most commonly used question types
117
+ in EDSL. It prompts an agent or language model to provide a textual response
118
+ without any specific structure or constraints on the format. The response can
119
+ be of any length and content, making it suitable for open-ended questions,
120
+ explanations, storytelling, and other scenarios requiring unrestricted text.
121
+
122
+ Attributes:
123
+ question_type (str): Identifier for this question type, set to "free_text".
124
+ _response_model: Pydantic model for validating responses.
125
+ response_validator_class: Class used to validate and fix responses.
126
+
127
+ Examples:
128
+ >>> q = QuestionFreeText(
129
+ ... question_name="opinion",
130
+ ... question_text="What do you think about AI?"
131
+ ... )
132
+ >>> q.question_type
133
+ 'free_text'
134
+
135
+ >>> from edsl.language_models import Model
136
+ >>> model = Model("test", canned_response="I think AI is fascinating.")
137
+ >>> result = q.by(model).run(disable_remote_inference=True)
138
+ >>> answer = result.select("answer.*").to_list()[0]
139
+ >>> "fascinating" in answer
140
+ True
141
+ """
142
+
143
+ question_type = "free_text"
144
+ _response_model = FreeTextResponse
145
+ response_validator_class = FreeTextResponseValidator
146
+
147
+ def __init__(
148
+ self,
149
+ question_name: str,
150
+ question_text: str,
151
+ answering_instructions: Optional[str] = None,
152
+ question_presentation: Optional[str] = None,
153
+ ):
154
+ """
155
+ Initialize a new free text question.
156
+
157
+ Args:
158
+ question_name: Identifier for the question, used in results and templates.
159
+ Must be a valid Python variable name.
160
+ question_text: The actual text of the question to be asked.
161
+ answering_instructions: Optional additional instructions for answering
162
+ the question, overrides default instructions.
163
+ question_presentation: Optional custom presentation template for the
164
+ question, overrides default presentation.
165
+
166
+ Examples:
167
+ >>> q = QuestionFreeText(
168
+ ... question_name="feedback",
169
+ ... question_text="Please provide your thoughts on this product."
170
+ ... )
171
+ >>> q.question_name
172
+ 'feedback'
173
+
174
+ >>> q = QuestionFreeText(
175
+ ... question_name="explanation",
176
+ ... question_text="Explain how photosynthesis works.",
177
+ ... answering_instructions="Provide a detailed scientific explanation."
178
+ ... )
179
+ """
180
+ self.question_name = question_name
181
+ self.question_text = question_text
182
+ self.answering_instructions = answering_instructions
183
+ self.question_presentation = question_presentation
184
+
185
+ @property
186
+ def question_html_content(self) -> str:
187
+ """
188
+ Generate HTML content for rendering the question in web interfaces.
189
+
190
+ This property generates HTML markup for the question when it needs to be
191
+ displayed in web interfaces or HTML contexts. For a free text question,
192
+ this is typically a textarea element.
193
+
194
+ Returns:
195
+ str: HTML markup for rendering the question.
196
+
197
+ Notes:
198
+ - Uses Jinja2 templating to generate the HTML
199
+ - Creates a textarea input element with the question_name as the ID and name
200
+ - Can be used for displaying the question in web UIs or HTML exports
201
+ """
202
+ from jinja2 import Template
203
+
204
+ question_html_content = Template(
205
+ """
206
+ <div>
207
+ <textarea id="{{ question_name }}" name="{{ question_name }}"></textarea>
208
+ </div>
209
+ """
210
+ ).render(question_name=self.question_name)
211
+ return question_html_content
212
+
213
+ @classmethod
214
+ @inject_exception
215
+ def example(cls, randomize: bool = False) -> "QuestionFreeText":
216
+ """
217
+ Create an example instance of a free text question.
218
+
219
+ This class method creates a predefined example of a free text question
220
+ for demonstration, testing, and documentation purposes.
221
+
222
+ Args:
223
+ randomize: If True, appends a random UUID to the question text to
224
+ ensure uniqueness in tests and examples.
225
+
226
+ Returns:
227
+ QuestionFreeText: An example free text question.
228
+
229
+ Examples:
230
+ >>> q = QuestionFreeText.example()
231
+ >>> q.question_name
232
+ 'how_are_you'
233
+ >>> q.question_text
234
+ 'How are you?'
235
+
236
+ >>> q1 = QuestionFreeText.example(randomize=True)
237
+ >>> q2 = QuestionFreeText.example(randomize=True)
238
+ >>> q1.question_text != q2.question_text
239
+ True
240
+ """
241
+ addition = "" if not randomize else str(uuid4())
242
+ return cls(question_name="how_are_you", question_text=f"How are you?{addition}")
243
+
244
+
245
+ def main():
246
+ """
247
+ Demonstrate the functionality of the QuestionFreeText class.
248
+
249
+ This function creates an example free text question and demonstrates its
250
+ key features including validation, serialization, and answer simulation.
251
+ It's primarily intended for testing and development purposes.
252
+
253
+ Note:
254
+ This function will be executed when the module is run directly,
255
+ but not when imported.
256
+ """
257
+ from .question_free_text import QuestionFreeText
258
+
259
+ # Create an example question
260
+ q = QuestionFreeText.example()
261
+ print(f"Question text: {q.question_text}")
262
+ print(f"Question name: {q.question_name}")
263
+
264
+ # Validate an answer
265
+ valid_answer = {"answer": "I like custard", "generated_tokens": "I like custard"}
266
+ validated = q._validate_answer(valid_answer)
267
+ print(f"Validated answer: {validated}")
268
+
269
+ # Simulate an answer
270
+ simulated = q._simulate_answer()
271
+ print(f"Simulated answer: {simulated}")
272
+
273
+ # Serialization demonstration
274
+ serialized = q.to_dict()
275
+ print(f"Serialized: {serialized}")
276
+ deserialized = QuestionBase.from_dict(serialized)
277
+ print(f"Deserialization successful: {deserialized.question_text == q.question_text}")
278
+
279
+ # Run doctests
280
+ import doctest
281
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
282
+ print("Doctests completed")
@@ -1,10 +1,11 @@
1
+ from __future__ import annotations
1
2
  from typing import Optional, Callable
2
3
  import inspect
3
4
 
4
- from edsl.questions.QuestionBase import QuestionBase
5
+ from .question_base import QuestionBase
5
6
 
6
- from edsl.utilities.restricted_python import create_restricted_function
7
- from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
7
+ from ..utilities.restricted_python import create_restricted_function
8
+ from ..utilities.decorators import add_edsl_version, remove_edsl_version
8
9
 
9
10
 
10
11
  class QuestionFunctional(QuestionBase):
@@ -25,7 +26,7 @@ class QuestionFunctional(QuestionBase):
25
26
 
26
27
  # Serialize the question to a dictionary
27
28
 
28
- >>> from edsl.questions.QuestionBase import QuestionBase
29
+ >>> from .question_base import QuestionBase
29
30
  >>> new_question = QuestionBase.from_dict(question.to_dict())
30
31
  >>> results = new_question.by(scenario).by(agent).run(disable_remote_cache = True, disable_remote_inference = True)
31
32
  >>> results.select("answer.*").to_list()[0] == 150
@@ -146,7 +147,7 @@ def calculate_sum_and_multiply(scenario, agent_traits):
146
147
 
147
148
  def main():
148
149
  from edsl import Scenario, Agent
149
- from edsl.questions.QuestionFunctional import QuestionFunctional
150
+ from .question_functional import QuestionFunctional
150
151
 
151
152
  # Create an instance of QuestionFunctional with the new function
152
153
  question = QuestionFunctional.example()
@@ -5,12 +5,11 @@ from typing import Any, Optional, Union
5
5
  from pydantic import Field
6
6
  from json_repair import repair_json
7
7
 
8
- from edsl.exceptions.questions import QuestionAnswerValidationError
9
- from edsl.questions.QuestionBase import QuestionBase
10
- from edsl.questions.descriptors import IntegerOrNoneDescriptor
11
- from edsl.questions.decorators import inject_exception
12
- from edsl.questions.response_validator_abc import ResponseValidatorABC
13
-
8
+ from .exceptions import QuestionAnswerValidationError
9
+ from .question_base import QuestionBase
10
+ from .descriptors import IntegerOrNoneDescriptor
11
+ from .decorators import inject_exception
12
+ from .response_validator_abc import ResponseValidatorABC
14
13
 
15
14
  def convert_string(s: str) -> Union[float, int, str, dict]:
16
15
  """Convert a string to a more appropriate type if possible.
@@ -198,7 +197,7 @@ class QuestionList(QuestionBase):
198
197
 
199
198
  def main():
200
199
  """Create an example of a list question and demonstrate its functionality."""
201
- from edsl.questions.QuestionList import QuestionList
200
+ from edsl.questions import QuestionList
202
201
 
203
202
  q = QuestionList.example(max_list_items=5)
204
203
  q.question_text
@@ -4,15 +4,16 @@ from typing import Union, Optional, Dict, List, Any
4
4
  from pydantic import BaseModel, Field, field_validator
5
5
  from jinja2 import Template
6
6
  import random
7
- from edsl.questions.QuestionBase import QuestionBase
8
- from edsl.questions.descriptors import (
7
+ from .question_base import QuestionBase
8
+ from .descriptors import (
9
9
  QuestionOptionsDescriptor,
10
10
  OptionLabelDescriptor,
11
11
  QuestionTextDescriptor,
12
12
  )
13
- from edsl.questions.response_validator_abc import ResponseValidatorABC
14
- from edsl.questions.decorators import inject_exception
15
- from edsl.exceptions.questions import (
13
+ from .response_validator_abc import ResponseValidatorABC
14
+ from .decorators import inject_exception
15
+
16
+ from .exceptions import (
16
17
  QuestionAnswerValidationError,
17
18
  QuestionCreationValidationError,
18
19
  )