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
@@ -1,7 +1,26 @@
1
- # """The Jobs class is a collection of agents, scenarios and models and one survey."""
1
+ """
2
+ The Jobs module is the core orchestration component of the EDSL framework.
3
+
4
+ It provides functionality to define, configure, and execute computational jobs that
5
+ involve multiple agents, scenarios, models, and a survey. Jobs are the primary way
6
+ that users run large-scale experiments or simulations in EDSL.
7
+
8
+ The Jobs class handles:
9
+ 1. Organizing all components (agents, scenarios, models, survey)
10
+ 2. Configuring execution parameters
11
+ 3. Managing resources like caches and API keys
12
+ 4. Running interviews in parallel
13
+ 5. Collecting and structuring results
14
+
15
+ This module is designed to be used by both application developers and researchers
16
+ who need to run complex simulations with language models.
17
+ """
2
18
  from __future__ import annotations
3
19
  import asyncio
4
20
  from inspect import signature
21
+ from typing import Optional, Union, TypeVar, Callable, cast
22
+ from functools import wraps
23
+
5
24
  from typing import (
6
25
  Literal,
7
26
  Optional,
@@ -13,38 +32,36 @@ from typing import (
13
32
  Tuple,
14
33
  )
15
34
 
16
- from edsl.Base import Base
35
+ from ..base import Base
36
+ from ..utilities import remove_edsl_version
37
+ from ..coop import CoopServerResponseError
17
38
 
18
- from edsl.jobs.buckets.BucketCollection import BucketCollection
19
- from edsl.jobs.JobsPrompts import JobsPrompts
20
- from edsl.jobs.interviews.Interview import Interview
21
- from edsl.utilities.remove_edsl_version import remove_edsl_version
22
- from edsl.jobs.runners.JobsRunnerAsyncio import JobsRunnerAsyncio
23
- from edsl.data.RemoteCacheSync import RemoteCacheSync
24
- from edsl.exceptions.coop import CoopServerResponseError
39
+ from ..buckets import BucketCollection
40
+ from ..scenarios import Scenario, ScenarioList
41
+ from ..surveys import Survey
42
+ from ..interviews import Interview
43
+
44
+ from .jobs_pricing_estimation import JobsPrompts
45
+ from .remote_inference import JobsRemoteInferenceHandler
46
+ from .jobs_checks import JobsChecks
47
+ from .data_structures import RunEnvironment, RunParameters, RunConfig
48
+ from .check_survey_scenario_compatibility import CheckSurveyScenarioCompatibility
25
49
 
26
- from edsl.jobs.JobsChecks import JobsChecks
27
- from edsl.jobs.data_structures import RunEnvironment, RunParameters, RunConfig
28
50
 
29
51
  if TYPE_CHECKING:
30
- from edsl.agents.Agent import Agent
31
- from edsl.agents.AgentList import AgentList
32
- from edsl.language_models.LanguageModel import LanguageModel
33
- from edsl.scenarios.Scenario import Scenario
34
- from edsl.scenarios.ScenarioList import ScenarioList
35
- from edsl.surveys.Survey import Survey
36
- from edsl.results.Results import Results
37
- from edsl.results.Dataset import Dataset
38
- from edsl.language_models.ModelList import ModelList
39
- from edsl.data.Cache import Cache
40
- from edsl.language_models.key_management.KeyLookup import KeyLookup
41
- from edsl.jobs.JobsRemoteInferenceHandler import JobsRemoteInferenceHandler
52
+ from ..agents import Agent
53
+ from ..agents import AgentList
54
+ from ..language_models import LanguageModel
55
+ from ..scenarios import Scenario, ScenarioList
56
+ from ..surveys import Survey
57
+ from ..results import Results
58
+ from ..dataset import Dataset
59
+ from ..language_models import ModelList
60
+ from ..caching import Cache
61
+ from ..key_management import KeyLookup
42
62
 
43
63
  VisibilityType = Literal["private", "public", "unlisted"]
44
64
 
45
- from dataclasses import dataclass
46
- from typing import Optional, Union, TypeVar, Callable, cast
47
- from functools import wraps
48
65
 
49
66
  try:
50
67
  from typing import ParamSpec
@@ -56,13 +73,32 @@ P = ParamSpec("P")
56
73
  T = TypeVar("T")
57
74
 
58
75
 
59
- from edsl.jobs.check_survey_scenario_compatibility import (
60
- CheckSurveyScenarioCompatibility,
61
- )
62
76
 
63
77
 
64
78
  def with_config(f: Callable[P, T]) -> Callable[P, T]:
65
- "This decorator make it so that the run function parameters match the RunConfig dataclass."
79
+ """
80
+ Decorator that processes function parameters to match the RunConfig dataclass structure.
81
+
82
+ This decorator is used primarily with the run() and run_async() methods to provide
83
+ a consistent interface for job configuration while maintaining a clean API.
84
+
85
+ The decorator:
86
+ 1. Extracts environment-related parameters into a RunEnvironment instance
87
+ 2. Extracts execution-related parameters into a RunParameters instance
88
+ 3. Combines both into a single RunConfig object
89
+ 4. Passes this RunConfig to the decorated function as a keyword argument
90
+
91
+ Parameters:
92
+ f (Callable): The function to decorate, typically run() or run_async()
93
+
94
+ Returns:
95
+ Callable: A wrapped function that accepts all RunConfig parameters directly
96
+
97
+ Example:
98
+ @with_config
99
+ def run(self, *, config: RunConfig) -> Results:
100
+ # Function can now access config.parameters and config.environment
101
+ """
66
102
  parameter_fields = {
67
103
  name: field.default
68
104
  for name, field in RunParameters.__dataclass_fields__.items()
@@ -84,24 +120,33 @@ def with_config(f: Callable[P, T]) -> Callable[P, T]:
84
120
  config = RunConfig(environment=environment, parameters=parameters)
85
121
  return f(*args, config=config)
86
122
 
87
- # Update the wrapper's signature to include all RunConfig parameters
88
- # old_sig = signature(f)
89
- # wrapper.__signature__ = old_sig.replace(
90
- # parameters=list(old_sig.parameters.values())[:-1]
91
- # + [
92
- # old_sig.parameters["config"].replace(
93
- # default=parameter_fields[name], name=name
94
- # )
95
- # for name in combined
96
- # ]
97
- # )
98
-
99
123
  return cast(Callable[P, T], wrapper)
100
124
 
101
125
 
102
126
  class Jobs(Base):
103
127
  """
104
- A collection of agents, scenarios and models and one survey that creates 'interviews'
128
+ A collection of agents, scenarios, models, and a survey that orchestrates interviews.
129
+
130
+ The Jobs class is the central component for running large-scale experiments or simulations
131
+ in EDSL. It manages the execution of interviews where agents interact with surveys through
132
+ language models, possibly in different scenarios.
133
+
134
+ Key responsibilities:
135
+ 1. Managing collections of agents, scenarios, and models
136
+ 2. Configuring execution parameters (caching, API keys, etc.)
137
+ 3. Managing parallel execution of interviews
138
+ 4. Handling remote cache and inference capabilities
139
+ 5. Collecting and organizing results
140
+
141
+ A typical workflow involves:
142
+ 1. Creating a survey with questions
143
+ 2. Creating a Jobs instance with that survey
144
+ 3. Adding agents, scenarios, and models using the `by()` method
145
+ 4. Running the job with `run()` or `run_async()`
146
+ 5. Analyzing the results
147
+
148
+ Jobs implements a fluent interface pattern, where methods return self to allow
149
+ method chaining for concise, readable configuration.
105
150
  """
106
151
 
107
152
  __documentation__ = "https://docs.expectedparrot.com/en/latest/jobs.html"
@@ -109,29 +154,44 @@ class Jobs(Base):
109
154
  def __init__(
110
155
  self,
111
156
  survey: "Survey",
112
- agents: Optional[Union[list[Agent], AgentList]] = None,
113
- models: Optional[Union[ModelList, list[LanguageModel]]] = None,
114
- scenarios: Optional[Union[ScenarioList, list[Scenario]]] = None,
157
+ agents: Optional[Union[list["Agent"], "AgentList"]] = None,
158
+ models: Optional[Union["ModelList", list["LanguageModel"]]] = None,
159
+ scenarios: Optional[Union["ScenarioList", list["Scenario"]]] = None,
115
160
  ):
116
- """Initialize a Jobs instance.
117
-
118
- :param survey: the survey to be used in the job
119
- :param agents: a list of agents
120
- :param models: a list of models
121
- :param scenarios: a list of scenarios
122
-
123
-
124
- >>> from edsl.surveys.Survey import Survey
125
- >>> from edsl.questions.QuestionFreeText import QuestionFreeText
126
- >>> q = QuestionFreeText(question_name="name", question_text="What is your name?")
127
- >>> s = Survey(questions=[q])
128
- >>> j = Jobs(survey = s)
129
- >>> q = QuestionFreeText(question_name="{{ bad_name }}", question_text="What is your name?")
130
- >>> s = Survey(questions=[q])
131
- >>> j = Jobs(survey = s)
132
- Traceback (most recent call last):
133
- ...
134
- ValueError: At least some question names are not valid: ['{{ bad_name }}']
161
+ """Initialize a Jobs instance with a survey and optional components.
162
+
163
+ The Jobs constructor requires a survey and optionally accepts collections of
164
+ agents, models, and scenarios. If any of these optional components are not provided,
165
+ they can be added later using the `by()` method or will be automatically populated
166
+ with defaults when the job is run.
167
+
168
+ Parameters:
169
+ survey (Survey): The survey containing questions to be used in the job
170
+ agents (Union[list[Agent], AgentList], optional): The agents that will take the survey
171
+ models (Union[ModelList, list[LanguageModel]], optional): The language models to use
172
+ scenarios (Union[ScenarioList, list[Scenario]], optional): The scenarios to run
173
+
174
+ Raises:
175
+ ValueError: If the survey contains questions with invalid names
176
+ (e.g., names containing template variables)
177
+
178
+ Examples:
179
+ >>> from edsl.surveys import Survey
180
+ >>> from edsl.questions import QuestionFreeText
181
+ >>> q = QuestionFreeText(question_name="name", question_text="What is your name?")
182
+ >>> s = Survey(questions=[q])
183
+ >>> j = Jobs(survey = s)
184
+ >>> q = QuestionFreeText(question_name="{{ bad_name }}", question_text="What is your name?")
185
+ >>> s = Survey(questions=[q])
186
+ >>> j = Jobs(survey = s)
187
+ Traceback (most recent call last):
188
+ ...
189
+ ValueError: At least some question names are not valid: ['{{ bad_name }}']
190
+
191
+ Notes:
192
+ - The survey's questions must have valid names without templating variables
193
+ - If agents, models, or scenarios are not provided, defaults will be used when running
194
+ - Upon initialization, a RunConfig is created with default environment and parameters
135
195
  """
136
196
  self.run_config = RunConfig(
137
197
  environment=RunEnvironment(), parameters=RunParameters()
@@ -142,6 +202,8 @@ class Jobs(Base):
142
202
  self.scenarios: ScenarioList = scenarios
143
203
  self.models: ModelList = models
144
204
 
205
+ self._where_clauses = []
206
+
145
207
  try:
146
208
  assert self.survey.question_names_valid()
147
209
  except Exception as e:
@@ -162,7 +224,7 @@ class Jobs(Base):
162
224
  self.run_config.add_cache(cache)
163
225
  return self
164
226
 
165
- def using_bucket_collection(self, bucket_collection: BucketCollection) -> Jobs:
227
+ def using_bucket_collection(self, bucket_collection: 'BucketCollection') -> Jobs:
166
228
  """
167
229
  Add a BucketCollection to the job.
168
230
 
@@ -171,7 +233,7 @@ class Jobs(Base):
171
233
  self.run_config.add_bucket_collection(bucket_collection)
172
234
  return self
173
235
 
174
- def using_key_lookup(self, key_lookup: KeyLookup) -> Jobs:
236
+ def using_key_lookup(self, key_lookup: 'KeyLookup') -> Jobs:
175
237
  """
176
238
  Add a KeyLookup to the job.
177
239
 
@@ -186,8 +248,8 @@ class Jobs(Base):
186
248
 
187
249
  :param obj: the object to add
188
250
  """
189
- from edsl.data.Cache import Cache
190
- from edsl.language_models.key_management.KeyLookup import KeyLookup
251
+ from ..caching import Cache
252
+ from ..key_management import KeyLookup
191
253
 
192
254
  if isinstance(obj, Cache):
193
255
  self.using_cache(obj)
@@ -203,7 +265,7 @@ class Jobs(Base):
203
265
 
204
266
  @models.setter
205
267
  def models(self, value):
206
- from edsl.language_models.ModelList import ModelList
268
+ from ..language_models import ModelList
207
269
 
208
270
  if value:
209
271
  if not isinstance(value, ModelList):
@@ -225,7 +287,7 @@ class Jobs(Base):
225
287
 
226
288
  @agents.setter
227
289
  def agents(self, value):
228
- from edsl.agents.AgentList import AgentList
290
+ from ..agents import AgentList
229
291
 
230
292
  if value:
231
293
  if not isinstance(value, AgentList):
@@ -235,14 +297,23 @@ class Jobs(Base):
235
297
  else:
236
298
  self._agents = AgentList([])
237
299
 
300
+ def where(self, expression: str) -> Jobs:
301
+ """
302
+ Filter the agents, scenarios, and models based on a condition.
303
+
304
+ :param expression: a condition to filter the agents, scenarios, and models
305
+ """
306
+ self._where_clauses.append(expression)
307
+ return self
308
+
238
309
  @property
239
310
  def scenarios(self):
240
311
  return self._scenarios
241
312
 
242
313
  @scenarios.setter
243
314
  def scenarios(self, value):
244
- from edsl.scenarios.ScenarioList import ScenarioList
245
- from edsl.results.Dataset import Dataset
315
+ from ..scenarios import ScenarioList
316
+ from ..dataset import Dataset
246
317
 
247
318
  if value:
248
319
  if isinstance(
@@ -260,40 +331,58 @@ class Jobs(Base):
260
331
  def by(
261
332
  self,
262
333
  *args: Union[
263
- Agent,
264
- Scenario,
265
- LanguageModel,
334
+ "Agent",
335
+ "Scenario",
336
+ "LanguageModel",
266
337
  Sequence[Union["Agent", "Scenario", "LanguageModel"]],
267
338
  ],
268
- ) -> Jobs:
339
+ ) -> "Jobs":
269
340
  """
270
- Add Agents, Scenarios and LanguageModels to a job.
271
-
272
- :param args: objects or a sequence (list, tuple, ...) of objects of the same type
273
-
274
- If no objects of this type exist in the Jobs instance, it stores the new objects as a list in the corresponding attribute.
275
- Otherwise, it combines the new objects with existing objects using the object's `__add__` method.
276
-
277
- This 'by' is intended to create a fluent interface.
278
-
279
- >>> from edsl.surveys.Survey import Survey
280
- >>> from edsl.questions.QuestionFreeText import QuestionFreeText
281
- >>> q = QuestionFreeText(question_name="name", question_text="What is your name?")
282
- >>> j = Jobs(survey = Survey(questions=[q]))
283
- >>> j
284
- Jobs(survey=Survey(...), agents=AgentList([]), models=ModelList([]), scenarios=ScenarioList([]))
285
- >>> from edsl.agents.Agent import Agent; a = Agent(traits = {"status": "Sad"})
286
- >>> j.by(a).agents
287
- AgentList([Agent(traits = {'status': 'Sad'})])
288
-
289
-
341
+ Add agents, scenarios, and language models to a job using a fluent interface.
342
+
343
+ This method is the primary way to configure a Jobs instance with components.
344
+ It intelligently handles different types of objects and collections, making
345
+ it easy to build complex job configurations with a concise syntax.
346
+
347
+ Parameters:
348
+ *args: Objects or sequences of objects to add to the job.
349
+ Supported types are Agent, Scenario, LanguageModel, and sequences of these.
350
+
351
+ Returns:
352
+ Jobs: The Jobs instance (self) for method chaining
353
+
354
+ Examples:
355
+ >>> from edsl.surveys import Survey
356
+ >>> from edsl.questions import QuestionFreeText
357
+ >>> q = QuestionFreeText(question_name="name", question_text="What is your name?")
358
+ >>> j = Jobs(survey = Survey(questions=[q]))
359
+ >>> j
360
+ Jobs(survey=Survey(...), agents=AgentList([]), models=ModelList([]), scenarios=ScenarioList([]))
361
+ >>> from edsl.agents import Agent; a = Agent(traits = {"status": "Sad"})
362
+ >>> j.by(a).agents
363
+ AgentList([Agent(traits = {'status': 'Sad'})])
364
+
365
+ # Adding multiple components at once
366
+ >>> from edsl.language_models import Model
367
+ >>> from edsl.scenarios import Scenario
368
+ >>> j = Jobs.example()
369
+ >>> _ = j.by(Agent(traits={"mood": "happy"})).by(Model(temperature=0.7)).by(Scenario({"time": "morning"}))
370
+
371
+ # Adding a sequence of the same type
372
+ >>> agents = [Agent(traits={"age": i}) for i in range(5)]
373
+ >>> _ = j.by(agents)
374
+
290
375
  Notes:
291
- - all objects must implement the 'get_value', 'set_value', and `__add__` methods
292
- - agents: traits of new agents are combined with traits of existing agents. New and existing agents should not have overlapping traits, and do not increase the # agents in the instance
293
- - scenarios: traits of new scenarios are combined with traits of old existing. New scenarios will overwrite overlapping traits, and do not increase the number of scenarios in the instance
294
- - models: new models overwrite old models.
376
+ - All objects must implement 'get_value', 'set_value', and '__add__' methods
377
+ - Agent traits: When adding agents with traits to existing agents, the traits are
378
+ combined. Avoid overlapping trait names to prevent unexpected behavior.
379
+ - Scenario traits: When adding scenarios with traits to existing scenarios, new
380
+ traits overwrite existing ones with the same name.
381
+ - Models: New models with the same attributes will override existing models.
382
+ - The method detects object types automatically and routes them to the appropriate
383
+ collection (agents, scenarios, or models).
295
384
  """
296
- from edsl.jobs.JobsComponentConstructor import JobsComponentConstructor
385
+ from .jobs_component_constructor import JobsComponentConstructor
297
386
 
298
387
  return JobsComponentConstructor(self).by(*args)
299
388
 
@@ -305,7 +394,7 @@ class Jobs(Base):
305
394
  >>> Jobs.example().prompts()
306
395
  Dataset(...)
307
396
  """
308
- return JobsPrompts(self).prompts(iterations=iterations)
397
+ return JobsPrompts.from_jobs(self).prompts(iterations=iterations)
309
398
 
310
399
  def show_prompts(self, all: bool = False) -> None:
311
400
  """Print the prompts."""
@@ -348,7 +437,7 @@ class Jobs(Base):
348
437
  def estimate_job_cost_from_external_prices(
349
438
  self, price_lookup: dict, iterations: int = 1
350
439
  ) -> dict:
351
- return JobsPrompts(self).estimate_job_cost_from_external_prices(
440
+ return JobsPrompts.from_jobs(self).estimate_job_cost_from_external_prices(
352
441
  price_lookup, iterations
353
442
  )
354
443
 
@@ -360,9 +449,9 @@ class Jobs(Base):
360
449
  return job_results.compute_job_cost()
361
450
 
362
451
  def replace_missing_objects(self) -> None:
363
- from edsl.agents.Agent import Agent
364
- from edsl.language_models.model import Model
365
- from edsl.scenarios.Scenario import Scenario
452
+ from ..agents import Agent
453
+ from ..language_models.model import Model
454
+ from ..scenarios import Scenario
366
455
 
367
456
  self.agents = self.agents or [Agent()]
368
457
  self.models = self.models or [Model()]
@@ -377,7 +466,7 @@ class Jobs(Base):
377
466
  with us filling in defaults.
378
467
 
379
468
  """
380
- from edsl.jobs.InterviewsConstructor import InterviewsConstructor
469
+ from .jobs_interview_constructor import InterviewsConstructor
381
470
 
382
471
  self.replace_missing_objects()
383
472
  yield from InterviewsConstructor(
@@ -385,8 +474,12 @@ class Jobs(Base):
385
474
  ).create_interviews()
386
475
 
387
476
  def show_flow(self, filename: Optional[str] = None) -> None:
388
- """Show the flow of the survey."""
389
- from edsl.surveys.SurveyFlowVisualization import SurveyFlowVisualization
477
+ """Show the flow of the survey.
478
+
479
+ >>> from edsl.jobs import Jobs
480
+ >>> Jobs.example().show_flow()
481
+ """
482
+ from ..surveys import SurveyFlowVisualization
390
483
  if self.scenarios:
391
484
  scenario = self.scenarios[0]
392
485
  else:
@@ -462,7 +555,7 @@ class Jobs(Base):
462
555
  846655441787442972
463
556
 
464
557
  """
465
- from edsl.utilities.utilities import dict_hash
558
+ from ..utilities import dict_hash
466
559
 
467
560
  return dict_hash(self.to_dict(add_edsl_version=False))
468
561
 
@@ -470,8 +563,6 @@ class Jobs(Base):
470
563
  """Check if a Job is verbose. If so, print the message."""
471
564
  if self.run_config.parameters.verbose:
472
565
  print(message)
473
- # if hasattr(self, "verbose") and self.verbose:
474
- # print(message)
475
566
 
476
567
  def all_question_parameters(self) -> set:
477
568
  """Return all the fields in the questions in the survey.
@@ -488,7 +579,7 @@ class Jobs(Base):
488
579
  return False
489
580
  if not self.run_config.parameters.disable_remote_cache:
490
581
  try:
491
- from edsl.coop.coop import Coop
582
+ from ..coop import Coop
492
583
 
493
584
  user_edsl_settings = Coop().edsl_settings
494
585
  return user_edsl_settings.get("remote_caching", False)
@@ -513,9 +604,7 @@ class Jobs(Base):
513
604
  )
514
605
  return job_info
515
606
 
516
- def _create_remote_inference_handler(self) -> JobsRemoteInferenceHandler:
517
- from edsl.jobs.JobsRemoteInferenceHandler import JobsRemoteInferenceHandler
518
-
607
+ def _create_remote_inference_handler(self) -> 'JobsRemoteInferenceHandler':
519
608
  return JobsRemoteInferenceHandler(
520
609
  self, verbose=self.run_config.parameters.verbose
521
610
  )
@@ -524,8 +613,7 @@ class Jobs(Base):
524
613
  self,
525
614
  config: RunConfig,
526
615
  ) -> Union["Results", None]:
527
- from edsl.jobs.JobsRemoteInferenceHandler import JobsRemoteInferenceHandler
528
- from edsl.jobs.JobsRemoteInferenceHandler import RemoteJobInfo
616
+ from .remote_inference import RemoteJobInfo
529
617
 
530
618
  background = config.parameters.background
531
619
 
@@ -533,7 +621,7 @@ class Jobs(Base):
533
621
  if jh.use_remote_inference(self.run_config.parameters.disable_remote_inference):
534
622
  job_info: RemoteJobInfo = self._start_remote_inference_job(jh)
535
623
  if background:
536
- from edsl.results.Results import Results
624
+ from edsl.results import Results
537
625
 
538
626
  results = Results.from_job_info(job_info)
539
627
  return results
@@ -560,9 +648,9 @@ class Jobs(Base):
560
648
  async def _execute_with_remote_cache(self, run_job_async: bool) -> Results:
561
649
  use_remote_cache = self.use_remote_cache()
562
650
 
563
- from edsl.coop.coop import Coop
564
- from edsl.jobs.runners.JobsRunnerAsyncio import JobsRunnerAsyncio
565
- from edsl.data.Cache import Cache
651
+ from ..coop import Coop
652
+ from .jobs_runner_asyncio import JobsRunnerAsyncio
653
+ from ..caching import Cache
566
654
 
567
655
  assert isinstance(self.run_config.environment.cache, Cache)
568
656
 
@@ -609,12 +697,12 @@ class Jobs(Base):
609
697
  self.run_config.environment.cache is None
610
698
  or self.run_config.environment.cache is True
611
699
  ):
612
- from edsl.data.CacheHandler import CacheHandler
700
+ from ..caching import CacheHandler
613
701
 
614
702
  self.run_config.environment.cache = CacheHandler().get_cache()
615
703
 
616
704
  if self.run_config.environment.cache is False:
617
- from edsl.data.Cache import Cache
705
+ from ..caching import Cache
618
706
 
619
707
  self.run_config.environment.cache = Cache(immediate_write=False)
620
708
 
@@ -642,21 +730,50 @@ class Jobs(Base):
642
730
  @with_config
643
731
  def run(self, *, config: RunConfig) -> "Results":
644
732
  """
645
- Runs the Job: conducts Interviews and returns their results.
646
-
647
- :param n: How many times to run each interview
648
- :param progress_bar: Whether to show a progress bar
649
- :param stop_on_exception: Stops the job if an exception is raised
650
- :param check_api_keys: Raises an error if API keys are invalid
651
- :param verbose: Prints extra messages
652
- :param remote_cache_description: Specifies a description for this group of entries in the remote cache
653
- :param remote_inference_description: Specifies a description for the remote inference job
654
- :param remote_inference_results_visibility: The initial visibility of the Results object on Coop. This will only be used for remote jobs!
655
- :param disable_remote_cache: If True, the job will not use remote cache. This only works for local jobs!
656
- :param disable_remote_inference: If True, the job will not use remote inference
657
- :param cache: A Cache object to store results
658
- :param bucket_collection: A BucketCollection object to track API calls
659
- :param key_lookup: A KeyLookup object to manage API keys
733
+ Runs the job by conducting interviews and returns their results.
734
+
735
+ This is the main entry point for executing a job. It processes all interviews
736
+ (combinations of agents, scenarios, and models) and returns a Results object
737
+ containing all responses and metadata.
738
+
739
+ Parameters:
740
+ n (int): Number of iterations to run each interview (default: 1)
741
+ progress_bar (bool): Whether to show a progress bar (default: False)
742
+ stop_on_exception (bool): Whether to stop the job if an exception is raised (default: False)
743
+ check_api_keys (bool): Whether to verify API keys before running (default: False)
744
+ verbose (bool): Whether to print extra messages during execution (default: True)
745
+ print_exceptions (bool): Whether to print exceptions as they occur (default: True)
746
+ remote_cache_description (str, optional): Description for entries in the remote cache
747
+ remote_inference_description (str, optional): Description for the remote inference job
748
+ remote_inference_results_visibility (VisibilityType): Visibility of results on Coop ("private", "public", "unlisted")
749
+ disable_remote_cache (bool): Whether to disable the remote cache (default: False)
750
+ disable_remote_inference (bool): Whether to disable remote inference (default: False)
751
+ fresh (bool): Whether to ignore cache and force new results (default: False)
752
+ skip_retry (bool): Whether to skip retrying failed interviews (default: False)
753
+ raise_validation_errors (bool): Whether to raise validation errors (default: False)
754
+ background (bool): Whether to run in background mode (default: False)
755
+ job_uuid (str, optional): UUID for the job, used for tracking
756
+ cache (Cache, optional): Cache object to store results
757
+ bucket_collection (BucketCollection, optional): Object to track API calls
758
+ key_lookup (KeyLookup, optional): Object to manage API keys
759
+
760
+ Returns:
761
+ Results: A Results object containing all responses and metadata
762
+
763
+ Notes:
764
+ - This method will first try to use remote inference if available
765
+ - If remote inference is not available, it will run locally
766
+ - For long-running jobs, consider using progress_bar=True
767
+ - For maximum performance, ensure appropriate caching is configured
768
+
769
+ Example:
770
+ >>> from edsl.jobs import Jobs
771
+ >>> from edsl.caching import Cache
772
+ >>> job = Jobs.example()
773
+ >>> from edsl import Model
774
+ >>> m = Model('test')
775
+ >>> results = job.by(m).run(cache=Cache(), progress_bar=False, n=2, disable_remote_inference=True)
776
+ ...
660
777
  """
661
778
  potentially_completed_results = self._run(config)
662
779
 
@@ -668,21 +785,50 @@ class Jobs(Base):
668
785
  @with_config
669
786
  async def run_async(self, *, config: RunConfig) -> "Results":
670
787
  """
671
- Runs the Job: conducts Interviews and returns their results.
672
-
673
- :param n: How many times to run each interview
674
- :param progress_bar: Whether to show a progress bar
675
- :param stop_on_exception: Stops the job if an exception is raised
676
- :param check_api_keys: Raises an error if API keys are invalid
677
- :param verbose: Prints extra messages
678
- :param remote_cache_description: Specifies a description for this group of entries in the remote cache
679
- :param remote_inference_description: Specifies a description for the remote inference job
680
- :param remote_inference_results_visibility: The initial visibility of the Results object on Coop. This will only be used for remote jobs!
681
- :param disable_remote_cache: If True, the job will not use remote cache. This only works for local jobs!
682
- :param disable_remote_inference: If True, the job will not use remote inference
683
- :param cache: A Cache object to store results
684
- :param bucket_collection: A BucketCollection object to track API calls
685
- :param key_lookup: A KeyLookup object to manage API keys
788
+ Asynchronously runs the job by conducting interviews and returns their results.
789
+
790
+ This method is the asynchronous version of `run()`. It has the same functionality and
791
+ parameters but can be awaited in an async context for better integration with
792
+ asynchronous code.
793
+
794
+ Parameters:
795
+ n (int): Number of iterations to run each interview (default: 1)
796
+ progress_bar (bool): Whether to show a progress bar (default: False)
797
+ stop_on_exception (bool): Whether to stop the job if an exception is raised (default: False)
798
+ check_api_keys (bool): Whether to verify API keys before running (default: False)
799
+ verbose (bool): Whether to print extra messages during execution (default: True)
800
+ print_exceptions (bool): Whether to print exceptions as they occur (default: True)
801
+ remote_cache_description (str, optional): Description for entries in the remote cache
802
+ remote_inference_description (str, optional): Description for the remote inference job
803
+ remote_inference_results_visibility (VisibilityType): Visibility of results on Coop ("private", "public", "unlisted")
804
+ disable_remote_cache (bool): Whether to disable the remote cache (default: False)
805
+ disable_remote_inference (bool): Whether to disable remote inference (default: False)
806
+ fresh (bool): Whether to ignore cache and force new results (default: False)
807
+ skip_retry (bool): Whether to skip retrying failed interviews (default: False)
808
+ raise_validation_errors (bool): Whether to raise validation errors (default: False)
809
+ background (bool): Whether to run in background mode (default: False)
810
+ job_uuid (str, optional): UUID for the job, used for tracking
811
+ cache (Cache, optional): Cache object to store results
812
+ bucket_collection (BucketCollection, optional): Object to track API calls
813
+ key_lookup (KeyLookup, optional): Object to manage API keys
814
+
815
+ Returns:
816
+ Results: A Results object containing all responses and metadata
817
+
818
+ Notes:
819
+ - This method should be used in async contexts (e.g., with `await`)
820
+ - For non-async contexts, use the `run()` method instead
821
+ - This method is particularly useful in notebook environments or async applications
822
+
823
+ Example:
824
+ >>> import asyncio
825
+ >>> from edsl.jobs import Jobs
826
+ >>> from edsl.caching import Cache
827
+ >>> job = Jobs.example()
828
+ >>> # In an async context
829
+ >>> async def run_job():
830
+ ... results = await job.run_async(cache=Cache(), progress_bar=True)
831
+ ... return results
686
832
  """
687
833
  self._run(config)
688
834
 
@@ -746,10 +892,10 @@ class Jobs(Base):
746
892
  @remove_edsl_version
747
893
  def from_dict(cls, data: dict) -> Jobs:
748
894
  """Creates a Jobs instance from a dictionary."""
749
- from edsl.surveys.Survey import Survey
750
- from edsl.agents.Agent import Agent
751
- from edsl.language_models.LanguageModel import LanguageModel
752
- from edsl.scenarios.Scenario import Scenario
895
+ from ..surveys import Survey
896
+ from ..agents import Agent
897
+ from ..language_models import LanguageModel
898
+ from ..scenarios import Scenario
753
899
 
754
900
  return cls(
755
901
  survey=Survey.from_dict(data["survey"]),
@@ -787,14 +933,14 @@ class Jobs(Base):
787
933
  """
788
934
  import random
789
935
  from uuid import uuid4
790
- from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
791
- from edsl.agents.Agent import Agent
792
- from edsl.scenarios.Scenario import Scenario
936
+ from ..questions import QuestionMultipleChoice
937
+ from ..agents import Agent
938
+ from ..scenarios import Scenario
793
939
 
794
940
  addition = "" if not randomize else str(uuid4())
795
941
 
796
942
  if test_model:
797
- from edsl.language_models.LanguageModel import LanguageModel
943
+ from ..language_models import LanguageModel
798
944
 
799
945
  m = LanguageModel.example(test_model=True)
800
946
 
@@ -835,8 +981,8 @@ class Jobs(Base):
835
981
  question_options=["Good", "Great", "OK", "Terrible"],
836
982
  question_name="how_feeling_yesterday",
837
983
  )
838
- from edsl.surveys.Survey import Survey
839
- from edsl.scenarios.ScenarioList import ScenarioList
984
+ from ..surveys import Survey
985
+ from ..scenarios import ScenarioList
840
986
 
841
987
  base_survey = Survey(questions=[q1, q2])
842
988
 
@@ -860,8 +1006,8 @@ class Jobs(Base):
860
1006
 
861
1007
  def main():
862
1008
  """Run the module's doctests."""
863
- from edsl.jobs.Jobs import Jobs
864
- from edsl.data.Cache import Cache
1009
+ from .jobs import Jobs
1010
+ from ..caching import Cache
865
1011
 
866
1012
  job = Jobs.example()
867
1013
  len(job) == 4