edsl 0.1.30.dev4__tar.gz → 0.1.31__tar.gz

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 (209) hide show
  1. {edsl-0.1.30.dev4 → edsl-0.1.31}/PKG-INFO +4 -2
  2. edsl-0.1.31/edsl/__version__.py +1 -0
  3. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/Invigilator.py +7 -2
  4. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/PromptConstructionMixin.py +18 -1
  5. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/config.py +4 -0
  6. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/Conjure.py +6 -0
  7. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/coop/coop.py +4 -0
  8. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/coop/utils.py +9 -1
  9. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/CacheHandler.py +3 -4
  10. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/enums.py +2 -0
  11. edsl-0.1.31/edsl/inference_services/DeepInfraService.py +18 -0
  12. edsl-0.1.31/edsl/inference_services/GroqService.py +18 -0
  13. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/InferenceServicesCollection.py +13 -5
  14. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/OpenAIService.py +64 -21
  15. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/registry.py +2 -1
  16. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/Jobs.py +80 -33
  17. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/buckets/TokenBucket.py +24 -5
  18. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/Interview.py +122 -75
  19. edsl-0.1.31/edsl/jobs/interviews/InterviewExceptionEntry.py +101 -0
  20. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewTaskBuildingMixin.py +58 -52
  21. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/interview_exception_tracking.py +68 -10
  22. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/runners/JobsRunnerAsyncio.py +112 -81
  23. edsl-0.1.31/edsl/jobs/runners/JobsRunnerStatusMixin.py +333 -0
  24. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/QuestionTaskCreator.py +1 -5
  25. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/TaskCreators.py +8 -2
  26. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/TaskHistory.py +145 -1
  27. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/LanguageModel.py +135 -75
  28. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/ModelList.py +8 -2
  29. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/registry.py +16 -0
  30. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionFunctional.py +34 -2
  31. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionMultipleChoice.py +58 -8
  32. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionNumerical.py +0 -1
  33. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/descriptors.py +42 -2
  34. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/DatasetExportMixin.py +258 -75
  35. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/Result.py +53 -5
  36. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/Results.py +66 -27
  37. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/ResultsToolsMixin.py +1 -1
  38. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/Scenario.py +14 -0
  39. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/ScenarioList.py +59 -21
  40. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/ScenarioListExportMixin.py +16 -5
  41. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/ScenarioListPdfMixin.py +3 -0
  42. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/study/Study.py +2 -2
  43. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/Survey.py +35 -1
  44. edsl-0.1.31/edsl/utilities/gcp_bucket/__init__.py +0 -0
  45. {edsl-0.1.30.dev4 → edsl-0.1.31}/pyproject.toml +22 -19
  46. edsl-0.1.30.dev4/edsl/__version__.py +0 -1
  47. edsl-0.1.30.dev4/edsl/inference_services/DeepInfraService.py +0 -103
  48. edsl-0.1.30.dev4/edsl/jobs/runners/JobsRunnerStatusData.py +0 -237
  49. edsl-0.1.30.dev4/edsl/jobs/runners/JobsRunnerStatusMixin.py +0 -77
  50. {edsl-0.1.30.dev4 → edsl-0.1.31}/LICENSE +0 -0
  51. {edsl-0.1.30.dev4 → edsl-0.1.31}/README.md +0 -0
  52. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/Base.py +0 -0
  53. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/BaseDiff.py +0 -0
  54. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/__init__.py +0 -0
  55. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/Agent.py +0 -0
  56. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/AgentList.py +0 -0
  57. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/InvigilatorBase.py +0 -0
  58. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/__init__.py +0 -0
  59. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/agents/descriptors.py +0 -0
  60. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/base/Base.py +0 -0
  61. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/AgentConstructionMixin.py +0 -0
  62. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputData.py +0 -0
  63. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputDataCSV.py +0 -0
  64. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputDataMixinQuestionStats.py +0 -0
  65. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputDataPyRead.py +0 -0
  66. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputDataSPSS.py +0 -0
  67. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/InputDataStata.py +0 -0
  68. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/QuestionOptionMixin.py +0 -0
  69. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/QuestionTypeMixin.py +0 -0
  70. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/RawQuestion.py +0 -0
  71. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/SurveyResponses.py +0 -0
  72. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/__init__.py +0 -0
  73. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/examples/placeholder.txt +0 -0
  74. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/naming_utilities.py +0 -0
  75. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conjure/utilities.py +0 -0
  76. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conversation/Conversation.py +0 -0
  77. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conversation/car_buying.py +0 -0
  78. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conversation/mug_negotiation.py +0 -0
  79. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/conversation/next_speaker_utilities.py +0 -0
  80. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/coop/__init__.py +0 -0
  81. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/Cache.py +0 -0
  82. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/CacheEntry.py +0 -0
  83. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/SQLiteDict.py +0 -0
  84. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/__init__.py +0 -0
  85. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data/orm.py +0 -0
  86. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/data_transfer_models.py +0 -0
  87. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/__init__.py +0 -0
  88. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/agents.py +0 -0
  89. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/configuration.py +0 -0
  90. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/coop.py +0 -0
  91. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/data.py +0 -0
  92. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/general.py +0 -0
  93. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/jobs.py +0 -0
  94. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/language_models.py +0 -0
  95. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/prompts.py +0 -0
  96. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/questions.py +0 -0
  97. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/results.py +0 -0
  98. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/exceptions/surveys.py +0 -0
  99. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/AnthropicService.py +0 -0
  100. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/GoogleService.py +0 -0
  101. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/InferenceServiceABC.py +0 -0
  102. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/__init__.py +0 -0
  103. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/models_available_cache.py +0 -0
  104. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/rate_limits_cache.py +0 -0
  105. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/inference_services/write_available.py +0 -0
  106. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/Answers.py +0 -0
  107. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/__init__.py +0 -0
  108. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/buckets/BucketCollection.py +0 -0
  109. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/buckets/ModelBuckets.py +0 -0
  110. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewStatistic.py +0 -0
  111. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -0
  112. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewStatusDictionary.py +0 -0
  113. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewStatusLog.py +0 -0
  114. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/InterviewStatusMixin.py +0 -0
  115. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/ReportErrors.py +0 -0
  116. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/interview_status_enum.py +0 -0
  117. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/interviews/retry_management.py +0 -0
  118. /edsl-0.1.30.dev4/edsl/questions/derived/__init__.py → /edsl-0.1.31/edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  119. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/TaskStatusLog.py +0 -0
  120. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/task_management.py +0 -0
  121. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tasks/task_status_enum.py +0 -0
  122. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tokens/InterviewTokenUsage.py +0 -0
  123. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/jobs/tokens/TokenUsage.py +0 -0
  124. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/RegisterLanguageModelsMeta.py +0 -0
  125. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/__init__.py +0 -0
  126. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/repair.py +0 -0
  127. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/language_models/unused/ReplicateBase.py +0 -0
  128. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/notebooks/Notebook.py +0 -0
  129. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/notebooks/__init__.py +0 -0
  130. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/Prompt.py +0 -0
  131. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/QuestionInstructionsBase.py +0 -0
  132. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/__init__.py +0 -0
  133. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/agent_instructions.py +0 -0
  134. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/agent_persona.py +0 -0
  135. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_budget.py +0 -0
  136. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_checkbox.py +0 -0
  137. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_extract.py +0 -0
  138. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_freetext.py +0 -0
  139. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_linear_scale.py +0 -0
  140. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_list.py +0 -0
  141. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_multiple_choice.py +0 -0
  142. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_numerical.py +0 -0
  143. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/library/question_rank.py +0 -0
  144. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/prompt_config.py +0 -0
  145. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/prompts/registry.py +0 -0
  146. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/AnswerValidatorMixin.py +0 -0
  147. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionBase.py +0 -0
  148. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionBudget.py +0 -0
  149. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionCheckBox.py +0 -0
  150. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionExtract.py +0 -0
  151. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionFreeText.py +0 -0
  152. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionList.py +0 -0
  153. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/QuestionRank.py +0 -0
  154. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/RegisterQuestionsMeta.py +0 -0
  155. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/SimpleAskMixin.py +0 -0
  156. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/__init__.py +0 -0
  157. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/compose_questions.py +0 -0
  158. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/derived/QuestionLikertFive.py +0 -0
  159. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/derived/QuestionLinearScale.py +0 -0
  160. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/derived/QuestionTopK.py +0 -0
  161. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/derived/QuestionYesNo.py +0 -0
  162. {edsl-0.1.30.dev4/edsl/utilities/gcp_bucket → edsl-0.1.31/edsl/questions/derived}/__init__.py +0 -0
  163. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/question_registry.py +0 -0
  164. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/questions/settings.py +0 -0
  165. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/Dataset.py +0 -0
  166. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/ResultsDBMixin.py +0 -0
  167. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/ResultsExportMixin.py +0 -0
  168. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/ResultsFetchMixin.py +0 -0
  169. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/ResultsGGMixin.py +0 -0
  170. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/results/__init__.py +0 -0
  171. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/FileStore.py +0 -0
  172. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/ScenarioHtmlMixin.py +0 -0
  173. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/ScenarioImageMixin.py +0 -0
  174. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/scenarios/__init__.py +0 -0
  175. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/shared.py +0 -0
  176. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/study/ObjectEntry.py +0 -0
  177. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/study/ProofOfWork.py +0 -0
  178. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/study/SnapShot.py +0 -0
  179. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/study/__init__.py +0 -0
  180. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/DAG.py +0 -0
  181. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/Memory.py +0 -0
  182. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/MemoryPlan.py +0 -0
  183. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/Rule.py +0 -0
  184. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/RuleCollection.py +0 -0
  185. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/SurveyCSS.py +0 -0
  186. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/SurveyExportMixin.py +0 -0
  187. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/SurveyFlowVisualizationMixin.py +0 -0
  188. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/__init__.py +0 -0
  189. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/base.py +0 -0
  190. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/surveys/descriptors.py +0 -0
  191. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/__init__.py +0 -0
  192. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/clusters.py +0 -0
  193. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/embeddings.py +0 -0
  194. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/embeddings_plotting.py +0 -0
  195. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/plotting.py +0 -0
  196. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/tools/summarize.py +0 -0
  197. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/SystemInfo.py +0 -0
  198. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/__init__.py +0 -0
  199. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/ast_utilities.py +0 -0
  200. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/data/Registry.py +0 -0
  201. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/data/__init__.py +0 -0
  202. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/data/scooter_results.json +0 -0
  203. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/decorators.py +0 -0
  204. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/gcp_bucket/cloud_storage.py +0 -0
  205. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/gcp_bucket/simple_example.py +0 -0
  206. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/interface.py +0 -0
  207. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/repair_functions.py +0 -0
  208. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/restricted_python.py +0 -0
  209. {edsl-0.1.30.dev4 → edsl-0.1.31}/edsl/utilities/utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edsl
3
- Version: 0.1.30.dev4
3
+ Version: 0.1.31
4
4
  Summary: Create and analyze LLM-based surveys
5
5
  Home-page: https://www.expectedparrot.com/
6
6
  License: MIT
@@ -19,10 +19,11 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
20
20
  Requires-Dist: anthropic (>=0.23.1,<0.24.0)
21
21
  Requires-Dist: black[jupyter] (>=24.4.2,<25.0.0)
22
+ Requires-Dist: groq (>=0.9.0,<0.10.0)
22
23
  Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
23
24
  Requires-Dist: jupyter (>=1.0.0,<2.0.0)
24
25
  Requires-Dist: markdown2 (>=2.4.11,<3.0.0)
25
- Requires-Dist: matplotlib (>=3.8.4,<4.0.0)
26
+ Requires-Dist: matplotlib (>=3.8,<3.9)
26
27
  Requires-Dist: nest-asyncio (>=1.5.9,<2.0.0)
27
28
  Requires-Dist: numpy (>=1.22,<2.0)
28
29
  Requires-Dist: openai (>=1.4.0,<2.0.0)
@@ -35,6 +36,7 @@ Requires-Dist: python-docx (>=1.1.0,<2.0.0)
35
36
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
36
37
  Requires-Dist: restrictedpython (>=7.1,<8.0)
37
38
  Requires-Dist: rich (>=13.7.0,<14.0.0)
39
+ Requires-Dist: setuptools (<72.0)
38
40
  Requires-Dist: simpleeval (>=0.9.13,<0.10.0)
39
41
  Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
40
42
  Requires-Dist: tenacity (>=8.2.3,<9.0.0)
@@ -0,0 +1 @@
1
+ __version__ = "0.1.31"
@@ -18,7 +18,12 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
18
18
  """An invigilator that uses an AI model to answer questions."""
19
19
 
20
20
  async def async_answer_question(self) -> AgentResponseDict:
21
- """Answer a question using the AI model."""
21
+ """Answer a question using the AI model.
22
+
23
+ >>> i = InvigilatorAI.example()
24
+ >>> i.answer_question()
25
+ {'message': '{"answer": "SPAM!"}'}
26
+ """
22
27
  params = self.get_prompts() | {"iteration": self.iteration}
23
28
  raw_response = await self.async_get_response(**params)
24
29
  data = {
@@ -29,6 +34,7 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
29
34
  "raw_model_response": raw_response["raw_model_response"],
30
35
  }
31
36
  response = self._format_raw_response(**data)
37
+ # breakpoint()
32
38
  return AgentResponseDict(**response)
33
39
 
34
40
  async def async_get_response(
@@ -97,7 +103,6 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
97
103
  answer = question._translate_answer_code_to_answer(
98
104
  response["answer"], combined_dict
99
105
  )
100
- # breakpoint()
101
106
  data = {
102
107
  "answer": answer,
103
108
  "comment": response.get(
@@ -275,8 +275,25 @@ class PromptConstructorMixin:
275
275
  if (new_question := question.split("_comment")[0]) in d:
276
276
  d[new_question].comment = answer
277
277
 
278
+ question_data = self.question.data.copy()
279
+
280
+ # check to see if the questio_options is actuall a string
281
+ if "question_options" in question_data:
282
+ if isinstance(self.question.data["question_options"], str):
283
+ from jinja2 import Environment, meta
284
+
285
+ env = Environment()
286
+ parsed_content = env.parse(self.question.data["question_options"])
287
+ question_option_key = list(
288
+ meta.find_undeclared_variables(parsed_content)
289
+ )[0]
290
+ question_data["question_options"] = self.scenario.get(
291
+ question_option_key
292
+ )
293
+
294
+ # breakpoint()
278
295
  rendered_instructions = question_prompt.render(
279
- self.question.data | self.scenario | d | {"agent": self.agent}
296
+ question_data | self.scenario | d | {"agent": self.agent}
280
297
  )
281
298
 
282
299
  undefined_template_variables = (
@@ -65,6 +65,10 @@ CONFIG_MAP = {
65
65
  # "default": None,
66
66
  # "info": "This env var holds your Anthropic API key (https://www.anthropic.com/).",
67
67
  # },
68
+ # "GROQ_API_KEY": {
69
+ # "default": None,
70
+ # "info": "This env var holds your GROQ API key (https://console.groq.com/login).",
71
+ # },
68
72
  }
69
73
 
70
74
 
@@ -35,6 +35,12 @@ class Conjure:
35
35
  # The __init__ method in Conjure won't be called because __new__ returns a different class instance.
36
36
  pass
37
37
 
38
+ @classmethod
39
+ def example(cls):
40
+ from edsl.conjure.InputData import InputDataABC
41
+
42
+ return InputDataABC.example()
43
+
38
44
 
39
45
  if __name__ == "__main__":
40
46
  pass
@@ -465,6 +465,7 @@ class Coop:
465
465
  description: Optional[str] = None,
466
466
  status: RemoteJobStatus = "queued",
467
467
  visibility: Optional[VisibilityType] = "unlisted",
468
+ iterations: Optional[int] = 1,
468
469
  ) -> dict:
469
470
  """
470
471
  Send a remote inference job to the server.
@@ -473,6 +474,7 @@ class Coop:
473
474
  :param optional description: A description for this entry in the remote cache.
474
475
  :param status: The status of the job. Should be 'queued', unless you are debugging.
475
476
  :param visibility: The visibility of the cache entry.
477
+ :param iterations: The number of times to run each interview.
476
478
 
477
479
  >>> job = Jobs.example()
478
480
  >>> coop.remote_inference_create(job=job, description="My job")
@@ -488,6 +490,7 @@ class Coop:
488
490
  ),
489
491
  "description": description,
490
492
  "status": status,
493
+ "iterations": iterations,
491
494
  "visibility": visibility,
492
495
  "version": self._edsl_version,
493
496
  },
@@ -498,6 +501,7 @@ class Coop:
498
501
  "uuid": response_json.get("jobs_uuid"),
499
502
  "description": response_json.get("description"),
500
503
  "status": response_json.get("status"),
504
+ "iterations": response_json.get("iterations"),
501
505
  "visibility": response_json.get("visibility"),
502
506
  "version": self._edsl_version,
503
507
  }
@@ -2,6 +2,7 @@ from edsl import (
2
2
  Agent,
3
3
  AgentList,
4
4
  Cache,
5
+ ModelList,
5
6
  Notebook,
6
7
  Results,
7
8
  Scenario,
@@ -9,6 +10,7 @@ from edsl import (
9
10
  Survey,
10
11
  Study,
11
12
  )
13
+ from edsl.language_models import LanguageModel
12
14
  from edsl.questions import QuestionBase
13
15
  from typing import Literal, Optional, Type, Union
14
16
 
@@ -16,6 +18,8 @@ EDSLObject = Union[
16
18
  Agent,
17
19
  AgentList,
18
20
  Cache,
21
+ LanguageModel,
22
+ ModelList,
19
23
  Notebook,
20
24
  Type[QuestionBase],
21
25
  Results,
@@ -29,6 +33,8 @@ ObjectType = Literal[
29
33
  "agent",
30
34
  "agent_list",
31
35
  "cache",
36
+ "model",
37
+ "model_list",
32
38
  "notebook",
33
39
  "question",
34
40
  "results",
@@ -62,8 +68,10 @@ class ObjectRegistry:
62
68
  {"object_type": "agent", "edsl_class": Agent},
63
69
  {"object_type": "agent_list", "edsl_class": AgentList},
64
70
  {"object_type": "cache", "edsl_class": Cache},
65
- {"object_type": "question", "edsl_class": QuestionBase},
71
+ {"object_type": "model", "edsl_class": LanguageModel},
72
+ {"object_type": "model_list", "edsl_class": ModelList},
66
73
  {"object_type": "notebook", "edsl_class": Notebook},
74
+ {"object_type": "question", "edsl_class": QuestionBase},
67
75
  {"object_type": "results", "edsl_class": Results},
68
76
  {"object_type": "scenario", "edsl_class": Scenario},
69
77
  {"object_type": "scenario_list", "edsl_class": ScenarioList},
@@ -41,7 +41,7 @@ class CacheHandler:
41
41
  old_data = self.from_old_sqlite_cache()
42
42
  self.cache.add_from_dict(old_data)
43
43
 
44
- def create_cache_directory(self) -> None:
44
+ def create_cache_directory(self, notify = False) -> None:
45
45
  """
46
46
  Create the cache directory if one is required and it does not exist.
47
47
  """
@@ -49,9 +49,8 @@ class CacheHandler:
49
49
  dir_path = os.path.dirname(path)
50
50
  if dir_path and not os.path.exists(dir_path):
51
51
  os.makedirs(dir_path)
52
- import warnings
53
-
54
- warnings.warn(f"Created cache directory: {dir_path}")
52
+ if notify:
53
+ print(f"Created cache directory: {dir_path}")
55
54
 
56
55
  def gen_cache(self) -> Cache:
57
56
  """
@@ -59,6 +59,7 @@ class InferenceServiceType(EnumWithChecks):
59
59
  GOOGLE = "google"
60
60
  TEST = "test"
61
61
  ANTHROPIC = "anthropic"
62
+ GROQ = "groq"
62
63
 
63
64
 
64
65
  service_to_api_keyname = {
@@ -69,6 +70,7 @@ service_to_api_keyname = {
69
70
  InferenceServiceType.GOOGLE.value: "GOOGLE_API_KEY",
70
71
  InferenceServiceType.TEST.value: "TBD",
71
72
  InferenceServiceType.ANTHROPIC.value: "ANTHROPIC_API_KEY",
73
+ InferenceServiceType.GROQ.value: "GROQ_API_KEY",
72
74
  }
73
75
 
74
76
 
@@ -0,0 +1,18 @@
1
+ import aiohttp
2
+ import json
3
+ import requests
4
+ from typing import Any, List
5
+
6
+ # from edsl.inference_services.InferenceServiceABC import InferenceServiceABC
7
+ from edsl.language_models import LanguageModel
8
+
9
+ from edsl.inference_services.OpenAIService import OpenAIService
10
+
11
+
12
+ class DeepInfraService(OpenAIService):
13
+ """DeepInfra service class."""
14
+
15
+ _inference_service_ = "deep_infra"
16
+ _env_key_name_ = "DEEP_INFRA_API_KEY"
17
+ _base_url_ = "https://api.deepinfra.com/v1/openai"
18
+ _models_list_cache: List[str] = []
@@ -0,0 +1,18 @@
1
+ from typing import Any, List
2
+ from edsl.inference_services.OpenAIService import OpenAIService
3
+
4
+ import groq
5
+
6
+
7
+ class GroqService(OpenAIService):
8
+ """DeepInfra service class."""
9
+
10
+ _inference_service_ = "groq"
11
+ _env_key_name_ = "GROQ_API_KEY"
12
+
13
+ _sync_client_ = groq.Groq
14
+ _async_client_ = groq.AsyncGroq
15
+
16
+ # _base_url_ = "https://api.deepinfra.com/v1/openai"
17
+ _base_url_ = None
18
+ _models_list_cache: List[str] = []
@@ -15,15 +15,19 @@ class InferenceServicesCollection:
15
15
  cls.added_models[service_name].append(model_name)
16
16
 
17
17
  @staticmethod
18
- def _get_service_available(service) -> list[str]:
18
+ def _get_service_available(service, warn: bool = False) -> list[str]:
19
19
  from_api = True
20
20
  try:
21
21
  service_models = service.available()
22
22
  except Exception as e:
23
- warnings.warn(
24
- f"Error getting models for {service._inference_service_}. Relying on cache.",
25
- UserWarning,
26
- )
23
+ if warn:
24
+ warnings.warn(
25
+ f"""Error getting models for {service._inference_service_}.
26
+ Check that you have properly stored your Expected Parrot API key and activated remote inference, or stored your own API keys for the language models that you want to use.
27
+ See https://docs.expectedparrot.com/en/latest/api_keys.html for instructions on storing API keys.
28
+ Relying on cache.""",
29
+ UserWarning,
30
+ )
27
31
  from edsl.inference_services.models_available_cache import models_available
28
32
 
29
33
  service_models = models_available.get(service._inference_service_, [])
@@ -57,4 +61,8 @@ class InferenceServicesCollection:
57
61
  if service_name is None or service_name == service._inference_service_:
58
62
  return service.create_model(model_name)
59
63
 
64
+ # if model_name == "test":
65
+ # from edsl.language_models import LanguageModel
66
+ # return LanguageModel(test = True)
67
+
60
68
  raise Exception(f"Model {model_name} not found in any of the services")
@@ -1,6 +1,9 @@
1
1
  from typing import Any, List
2
2
  import re
3
- from openai import AsyncOpenAI
3
+ import os
4
+
5
+ # from openai import AsyncOpenAI
6
+ import openai
4
7
 
5
8
  from edsl.inference_services.InferenceServiceABC import InferenceServiceABC
6
9
  from edsl.language_models import LanguageModel
@@ -12,6 +15,22 @@ class OpenAIService(InferenceServiceABC):
12
15
 
13
16
  _inference_service_ = "openai"
14
17
  _env_key_name_ = "OPENAI_API_KEY"
18
+ _base_url_ = None
19
+
20
+ _sync_client_ = openai.OpenAI
21
+ _async_client_ = openai.AsyncOpenAI
22
+
23
+ @classmethod
24
+ def sync_client(cls):
25
+ return cls._sync_client_(
26
+ api_key=os.getenv(cls._env_key_name_), base_url=cls._base_url_
27
+ )
28
+
29
+ @classmethod
30
+ def async_client(cls):
31
+ return cls._async_client_(
32
+ api_key=os.getenv(cls._env_key_name_), base_url=cls._base_url_
33
+ )
15
34
 
16
35
  # TODO: Make this a coop call
17
36
  model_exclude_list = [
@@ -31,16 +50,24 @@ class OpenAIService(InferenceServiceABC):
31
50
  ]
32
51
  _models_list_cache: List[str] = []
33
52
 
53
+ @classmethod
54
+ def get_model_list(cls):
55
+ raw_list = cls.sync_client().models.list()
56
+ if hasattr(raw_list, "data"):
57
+ return raw_list.data
58
+ else:
59
+ return raw_list
60
+
34
61
  @classmethod
35
62
  def available(cls) -> List[str]:
36
- from openai import OpenAI
63
+ # from openai import OpenAI
37
64
 
38
65
  if not cls._models_list_cache:
39
66
  try:
40
- client = OpenAI()
67
+ # client = OpenAI(api_key = os.getenv(cls._env_key_name_), base_url = cls._base_url_)
41
68
  cls._models_list_cache = [
42
69
  m.id
43
- for m in client.models.list()
70
+ for m in cls.get_model_list()
44
71
  if m.id not in cls.model_exclude_list
45
72
  ]
46
73
  except Exception as e:
@@ -78,15 +105,24 @@ class OpenAIService(InferenceServiceABC):
78
105
  "top_logprobs": 3,
79
106
  }
80
107
 
108
+ def sync_client(self):
109
+ return cls.sync_client()
110
+
111
+ def async_client(self):
112
+ return cls.async_client()
113
+
81
114
  @classmethod
82
115
  def available(cls) -> list[str]:
83
- client = openai.OpenAI()
84
- return client.models.list()
116
+ # import openai
117
+ # client = openai.OpenAI(api_key = os.getenv(cls._env_key_name_), base_url = cls._base_url_)
118
+ # return client.models.list()
119
+ return cls.sync_client().models.list()
85
120
 
86
121
  def get_headers(self) -> dict[str, Any]:
87
- from openai import OpenAI
122
+ # from openai import OpenAI
88
123
 
89
- client = OpenAI()
124
+ # client = OpenAI(api_key = os.getenv(cls._env_key_name_), base_url = cls._base_url_)
125
+ client = self.sync_client()
90
126
  response = client.chat.completions.with_raw_response.create(
91
127
  messages=[
92
128
  {
@@ -124,8 +160,8 @@ class OpenAIService(InferenceServiceABC):
124
160
  encoded_image=None,
125
161
  ) -> dict[str, Any]:
126
162
  """Calls the OpenAI API and returns the API response."""
127
- content = [{"type": "text", "text": user_prompt}]
128
163
  if encoded_image:
164
+ content = [{"type": "text", "text": user_prompt}]
129
165
  content.append(
130
166
  {
131
167
  "type": "image_url",
@@ -134,21 +170,28 @@ class OpenAIService(InferenceServiceABC):
134
170
  },
135
171
  }
136
172
  )
137
- self.client = AsyncOpenAI()
138
- response = await self.client.chat.completions.create(
139
- model=self.model,
140
- messages=[
173
+ else:
174
+ content = user_prompt
175
+ # self.client = AsyncOpenAI(
176
+ # api_key = os.getenv(cls._env_key_name_),
177
+ # base_url = cls._base_url_
178
+ # )
179
+ client = self.async_client()
180
+ params = {
181
+ "model": self.model,
182
+ "messages": [
141
183
  {"role": "system", "content": system_prompt},
142
184
  {"role": "user", "content": content},
143
185
  ],
144
- temperature=self.temperature,
145
- max_tokens=self.max_tokens,
146
- top_p=self.top_p,
147
- frequency_penalty=self.frequency_penalty,
148
- presence_penalty=self.presence_penalty,
149
- logprobs=self.logprobs,
150
- top_logprobs=self.top_logprobs if self.logprobs else None,
151
- )
186
+ "temperature": self.temperature,
187
+ "max_tokens": self.max_tokens,
188
+ "top_p": self.top_p,
189
+ "frequency_penalty": self.frequency_penalty,
190
+ "presence_penalty": self.presence_penalty,
191
+ "logprobs": self.logprobs,
192
+ "top_logprobs": self.top_logprobs if self.logprobs else None,
193
+ }
194
+ response = await client.chat.completions.create(**params)
152
195
  return response.model_dump()
153
196
 
154
197
  @staticmethod
@@ -6,7 +6,8 @@ from edsl.inference_services.OpenAIService import OpenAIService
6
6
  from edsl.inference_services.AnthropicService import AnthropicService
7
7
  from edsl.inference_services.DeepInfraService import DeepInfraService
8
8
  from edsl.inference_services.GoogleService import GoogleService
9
+ from edsl.inference_services.GroqService import GroqService
9
10
 
10
11
  default = InferenceServicesCollection(
11
- [OpenAIService, AnthropicService, DeepInfraService, GoogleService]
12
+ [OpenAIService, AnthropicService, DeepInfraService, GoogleService, GroqService]
12
13
  )
@@ -3,9 +3,7 @@ from __future__ import annotations
3
3
  import warnings
4
4
  from itertools import product
5
5
  from typing import Optional, Union, Sequence, Generator
6
-
7
6
  from edsl.Base import Base
8
-
9
7
  from edsl.exceptions import MissingAPIKeyError
10
8
  from edsl.jobs.buckets.BucketCollection import BucketCollection
11
9
  from edsl.jobs.interviews.Interview import Interview
@@ -321,7 +319,11 @@ class Jobs(Base):
321
319
  self.scenarios = self.scenarios or [Scenario()]
322
320
  for agent, scenario, model in product(self.agents, self.scenarios, self.models):
323
321
  yield Interview(
324
- survey=self.survey, agent=agent, scenario=scenario, model=model
322
+ survey=self.survey,
323
+ agent=agent,
324
+ scenario=scenario,
325
+ model=model,
326
+ skip_retry=self.skip_retry,
325
327
  )
326
328
 
327
329
  def create_bucket_collection(self) -> BucketCollection:
@@ -411,6 +413,12 @@ class Jobs(Base):
411
413
  if warn:
412
414
  warnings.warn(message)
413
415
 
416
+ @property
417
+ def skip_retry(self):
418
+ if not hasattr(self, "_skip_retry"):
419
+ return False
420
+ return self._skip_retry
421
+
414
422
  def run(
415
423
  self,
416
424
  n: int = 1,
@@ -425,6 +433,7 @@ class Jobs(Base):
425
433
  print_exceptions=True,
426
434
  remote_cache_description: Optional[str] = None,
427
435
  remote_inference_description: Optional[str] = None,
436
+ skip_retry: bool = False,
428
437
  ) -> Results:
429
438
  """
430
439
  Runs the Job: conducts Interviews and returns their results.
@@ -443,6 +452,7 @@ class Jobs(Base):
443
452
  from edsl.coop.coop import Coop
444
453
 
445
454
  self._check_parameters()
455
+ self._skip_retry = skip_retry
446
456
 
447
457
  if batch_mode is not None:
448
458
  raise NotImplementedError(
@@ -461,12 +471,11 @@ class Jobs(Base):
461
471
  remote_inference = False
462
472
 
463
473
  if remote_inference:
464
- from edsl.agents.Agent import Agent
465
- from edsl.language_models.registry import Model
466
- from edsl.results.Result import Result
467
- from edsl.results.Results import Results
468
- from edsl.scenarios.Scenario import Scenario
469
- from edsl.surveys.Survey import Survey
474
+ import time
475
+ from datetime import datetime
476
+ from edsl.config import CONFIG
477
+
478
+ expected_parrot_url = CONFIG.get("EXPECTED_PARROT_URL")
470
479
 
471
480
  self._output("Remote inference activated. Sending job to server...")
472
481
  if remote_cache:
@@ -474,33 +483,60 @@ class Jobs(Base):
474
483
  "Remote caching activated. The remote cache will be used for this job."
475
484
  )
476
485
 
477
- remote_job_data = coop.remote_inference_create(
486
+ remote_job_creation_data = coop.remote_inference_create(
478
487
  self,
479
488
  description=remote_inference_description,
480
489
  status="queued",
490
+ iterations=n,
481
491
  )
482
- self._output("Job sent!")
483
- # Create mock results object to store job data
484
- results = Results(
485
- survey=Survey(),
486
- data=[
487
- Result(
488
- agent=Agent.example(),
489
- scenario=Scenario.example(),
490
- model=Model(),
491
- iteration=1,
492
- answer={"info": "Remote job details"},
492
+ time_queued = datetime.now().strftime("%m/%d/%Y %I:%M:%S %p")
493
+ job_uuid = remote_job_creation_data.get("uuid")
494
+ print(f"Remote inference started (Job uuid={job_uuid}).")
495
+ # print(f"Job queued at {time_queued}.")
496
+ job_in_queue = True
497
+ while job_in_queue:
498
+ remote_job_data = coop.remote_inference_get(job_uuid)
499
+ status = remote_job_data.get("status")
500
+ if status == "cancelled":
501
+ print("\r" + " " * 80 + "\r", end="")
502
+ print("Job cancelled by the user.")
503
+ print(
504
+ f"See {expected_parrot_url}/home/remote-inference for more details."
493
505
  )
494
- ],
495
- )
496
- results.add_columns_from_dict([remote_job_data])
497
- if self.verbose:
498
- results.select(["info", "uuid", "status", "version"]).print(
499
- format="rich"
500
- )
501
- return results
506
+ return None
507
+ elif status == "failed":
508
+ print("\r" + " " * 80 + "\r", end="")
509
+ print("Job failed.")
510
+ print(
511
+ f"See {expected_parrot_url}/home/remote-inference for more details."
512
+ )
513
+ return None
514
+ elif status == "completed":
515
+ results_uuid = remote_job_data.get("results_uuid")
516
+ results = coop.get(results_uuid, expected_object_type="results")
517
+ print("\r" + " " * 80 + "\r", end="")
518
+ print(
519
+ f"Job completed and Results stored on Coop (Results uuid={results_uuid})."
520
+ )
521
+ return results
522
+ else:
523
+ duration = 10 if len(self) < 10 else 60
524
+ time_checked = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
525
+ frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
526
+ start_time = time.time()
527
+ i = 0
528
+ while time.time() - start_time < duration:
529
+ print(
530
+ f"\r{frames[i % len(frames)]} Job status: {status} - last update: {time_checked}",
531
+ end="",
532
+ flush=True,
533
+ )
534
+ time.sleep(0.1)
535
+ i += 1
502
536
  else:
503
537
  if check_api_keys:
538
+ from edsl import Model
539
+
504
540
  for model in self.models + [Model()]:
505
541
  if not model.has_valid_api_key():
506
542
  raise MissingAPIKeyError(
@@ -606,9 +642,9 @@ class Jobs(Base):
606
642
  results = JobsRunnerAsyncio(self).run(*args, **kwargs)
607
643
  return results
608
644
 
609
- async def run_async(self, cache=None, **kwargs):
645
+ async def run_async(self, cache=None, n=1, **kwargs):
610
646
  """Run the job asynchronously."""
611
- results = await JobsRunnerAsyncio(self).run_async(cache=cache, **kwargs)
647
+ results = await JobsRunnerAsyncio(self).run_async(cache=cache, n=n, **kwargs)
612
648
  return results
613
649
 
614
650
  def all_question_parameters(self):
@@ -688,7 +724,10 @@ class Jobs(Base):
688
724
  #######################
689
725
  @classmethod
690
726
  def example(
691
- cls, throw_exception_probability: int = 0, randomize: bool = False
727
+ cls,
728
+ throw_exception_probability: int = 0,
729
+ randomize: bool = False,
730
+ test_model=False,
692
731
  ) -> Jobs:
693
732
  """Return an example Jobs instance.
694
733
 
@@ -706,6 +745,11 @@ class Jobs(Base):
706
745
 
707
746
  addition = "" if not randomize else str(uuid4())
708
747
 
748
+ if test_model:
749
+ from edsl.language_models import LanguageModel
750
+
751
+ m = LanguageModel.example(test_model=True)
752
+
709
753
  # (status, question, period)
710
754
  agent_answers = {
711
755
  ("Joyful", "how_feeling", "morning"): "OK",
@@ -753,7 +797,10 @@ class Jobs(Base):
753
797
  Scenario({"period": "afternoon"}),
754
798
  ]
755
799
  )
756
- job = base_survey.by(scenario_list).by(joy_agent, sad_agent)
800
+ if test_model:
801
+ job = base_survey.by(m).by(scenario_list).by(joy_agent, sad_agent)
802
+ else:
803
+ job = base_survey.by(scenario_list).by(joy_agent, sad_agent)
757
804
 
758
805
  return job
759
806