edsl 0.1.36.dev2__tar.gz → 0.1.36.dev4__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 (294) hide show
  1. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/PKG-INFO +1 -1
  2. edsl-0.1.36.dev4/edsl/__version__.py +1 -0
  3. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/coop/PriceFetcher.py +14 -18
  4. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/coop/coop.py +7 -3
  5. edsl-0.1.36.dev4/edsl/data/RemoteCacheSync.py +84 -0
  6. edsl-0.1.36.dev4/edsl/exceptions/coop.py +10 -0
  7. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/Jobs.py +182 -165
  8. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/Interview.py +21 -3
  9. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewExceptionCollection.py +9 -0
  10. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewExceptionEntry.py +24 -6
  11. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/runners/JobsRunnerAsyncio.py +17 -23
  12. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tasks/TaskHistory.py +22 -7
  13. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/Result.py +11 -9
  14. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/Results.py +13 -1
  15. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/pyproject.toml +1 -1
  16. edsl-0.1.36.dev2/edsl/__version__.py +0 -1
  17. edsl-0.1.36.dev2/edsl/exceptions/coop.py +0 -2
  18. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/LICENSE +0 -0
  19. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/README.md +0 -0
  20. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/Base.py +0 -0
  21. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/BaseDiff.py +0 -0
  22. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/TemplateLoader.py +0 -0
  23. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/__init__.py +0 -0
  24. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/Agent.py +0 -0
  25. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/AgentList.py +0 -0
  26. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/Invigilator.py +0 -0
  27. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/InvigilatorBase.py +0 -0
  28. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/PromptConstructor.py +0 -0
  29. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/__init__.py +0 -0
  30. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/descriptors.py +0 -0
  31. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/agents/prompt_helpers.py +0 -0
  32. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/AutoStudy.py +0 -0
  33. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StageBase.py +0 -0
  34. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StageGenerateSurvey.py +0 -0
  35. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StageLabelQuestions.py +0 -0
  36. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StagePersona.py +0 -0
  37. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StagePersonaDimensionValueRanges.py +0 -0
  38. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StagePersonaDimensionValues.py +0 -0
  39. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StagePersonaDimensions.py +0 -0
  40. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/StageQuestions.py +0 -0
  41. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/SurveyCreatorPipeline.py +0 -0
  42. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/auto/utilities.py +0 -0
  43. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/base/Base.py +0 -0
  44. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/config.py +0 -0
  45. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/AgentConstructionMixin.py +0 -0
  46. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/Conjure.py +0 -0
  47. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputData.py +0 -0
  48. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputDataCSV.py +0 -0
  49. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputDataMixinQuestionStats.py +0 -0
  50. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputDataPyRead.py +0 -0
  51. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputDataSPSS.py +0 -0
  52. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/InputDataStata.py +0 -0
  53. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/QuestionOptionMixin.py +0 -0
  54. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/QuestionTypeMixin.py +0 -0
  55. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/RawQuestion.py +0 -0
  56. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/SurveyResponses.py +0 -0
  57. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/__init__.py +0 -0
  58. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/examples/placeholder.txt +0 -0
  59. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/naming_utilities.py +0 -0
  60. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conjure/utilities.py +0 -0
  61. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conversation/Conversation.py +0 -0
  62. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conversation/car_buying.py +0 -0
  63. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conversation/mug_negotiation.py +0 -0
  64. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/conversation/next_speaker_utilities.py +0 -0
  65. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/coop/__init__.py +0 -0
  66. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/coop/utils.py +0 -0
  67. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/Cache.py +0 -0
  68. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/CacheEntry.py +0 -0
  69. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/CacheHandler.py +0 -0
  70. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/SQLiteDict.py +0 -0
  71. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/__init__.py +0 -0
  72. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data/orm.py +0 -0
  73. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/data_transfer_models.py +0 -0
  74. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/enums.py +0 -0
  75. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/__init__.py +0 -0
  76. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/agents.py +0 -0
  77. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/configuration.py +0 -0
  78. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/data.py +0 -0
  79. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/general.py +0 -0
  80. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/jobs.py +0 -0
  81. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/language_models.py +0 -0
  82. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/prompts.py +0 -0
  83. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/questions.py +0 -0
  84. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/results.py +0 -0
  85. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/exceptions/surveys.py +0 -0
  86. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/AnthropicService.py +0 -0
  87. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/AwsBedrock.py +0 -0
  88. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/AzureAI.py +0 -0
  89. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/DeepInfraService.py +0 -0
  90. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/GoogleService.py +0 -0
  91. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/GroqService.py +0 -0
  92. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/InferenceServiceABC.py +0 -0
  93. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/InferenceServicesCollection.py +0 -0
  94. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/MistralAIService.py +0 -0
  95. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/OllamaService.py +0 -0
  96. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/OpenAIService.py +0 -0
  97. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/TestService.py +0 -0
  98. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/TogetherAIService.py +0 -0
  99. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/__init__.py +0 -0
  100. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/models_available_cache.py +0 -0
  101. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/rate_limits_cache.py +0 -0
  102. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/registry.py +0 -0
  103. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/inference_services/write_available.py +0 -0
  104. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/Answers.py +0 -0
  105. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/__init__.py +0 -0
  106. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/buckets/BucketCollection.py +0 -0
  107. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/buckets/ModelBuckets.py +0 -0
  108. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/buckets/TokenBucket.py +0 -0
  109. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewStatistic.py +0 -0
  110. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -0
  111. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewStatusDictionary.py +0 -0
  112. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/InterviewStatusLog.py +0 -0
  113. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/ReportErrors.py +0 -0
  114. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/interviews/interview_status_enum.py +0 -0
  115. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/runners/JobsRunnerStatus.py +0 -0
  116. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  117. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tasks/QuestionTaskCreator.py +0 -0
  118. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tasks/TaskCreators.py +0 -0
  119. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tasks/TaskStatusLog.py +0 -0
  120. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tasks/task_status_enum.py +0 -0
  121. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tokens/InterviewTokenUsage.py +0 -0
  122. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/jobs/tokens/TokenUsage.py +0 -0
  123. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/LanguageModel.py +0 -0
  124. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/ModelList.py +0 -0
  125. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/RegisterLanguageModelsMeta.py +0 -0
  126. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/__init__.py +0 -0
  127. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/fake_openai_call.py +0 -0
  128. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/fake_openai_service.py +0 -0
  129. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/registry.py +0 -0
  130. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/repair.py +0 -0
  131. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/unused/ReplicateBase.py +0 -0
  132. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/language_models/utilities.py +0 -0
  133. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/notebooks/Notebook.py +0 -0
  134. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/notebooks/__init__.py +0 -0
  135. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/prompts/Prompt.py +0 -0
  136. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/prompts/__init__.py +0 -0
  137. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/AnswerValidatorMixin.py +0 -0
  138. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionBase.py +0 -0
  139. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionBaseGenMixin.py +0 -0
  140. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionBasePromptsMixin.py +0 -0
  141. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionBudget.py +0 -0
  142. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionCheckBox.py +0 -0
  143. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionExtract.py +0 -0
  144. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionFreeText.py +0 -0
  145. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionFunctional.py +0 -0
  146. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionList.py +0 -0
  147. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionMultipleChoice.py +0 -0
  148. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionNumerical.py +0 -0
  149. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/QuestionRank.py +0 -0
  150. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/Quick.py +0 -0
  151. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/RegisterQuestionsMeta.py +0 -0
  152. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/ResponseValidatorABC.py +0 -0
  153. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/SimpleAskMixin.py +0 -0
  154. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/__init__.py +0 -0
  155. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/compose_questions.py +0 -0
  156. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/decorators.py +0 -0
  157. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/derived/QuestionLikertFive.py +0 -0
  158. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/derived/QuestionLinearScale.py +0 -0
  159. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/derived/QuestionTopK.py +0 -0
  160. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/derived/QuestionYesNo.py +0 -0
  161. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/derived/__init__.py +0 -0
  162. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/descriptors.py +0 -0
  163. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_budget.jinja +0 -0
  164. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_checkbox.jinja +0 -0
  165. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_extract.jinja +0 -0
  166. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_free_text.jinja +0 -0
  167. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_linear_scale.jinja +0 -0
  168. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_list.jinja +0 -0
  169. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_multiple_choice.jinja +0 -0
  170. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/prompt_templates/question_numerical.jinja +0 -0
  171. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/question_registry.py +0 -0
  172. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/settings.py +0 -0
  173. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/__init__.py +0 -0
  174. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  175. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/budget/__init__.py +0 -0
  176. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/budget/__pycache__/__init__.cpython-311.pyc +0 -0
  177. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/budget/answering_instructions.jinja +0 -0
  178. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/budget/question_presentation.jinja +0 -0
  179. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/checkbox/__init__.py +0 -0
  180. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/checkbox/__pycache__/__init__.cpython-311.pyc +0 -0
  181. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/checkbox/answering_instructions.jinja +0 -0
  182. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/checkbox/question_presentation.jinja +0 -0
  183. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/extract/__init__.py +0 -0
  184. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/extract/__pycache__/__init__.cpython-311.pyc +0 -0
  185. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/extract/answering_instructions.jinja +0 -0
  186. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/extract/question_presentation.jinja +0 -0
  187. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/free_text/__init__.py +0 -0
  188. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/free_text/__pycache__/__init__.cpython-311.pyc +0 -0
  189. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  190. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/free_text/question_presentation.jinja +0 -0
  191. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/likert_five/__init__.py +0 -0
  192. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/likert_five/__pycache__/__init__.cpython-311.pyc +0 -0
  193. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/likert_five/answering_instructions.jinja +0 -0
  194. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/likert_five/question_presentation.jinja +0 -0
  195. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/linear_scale/__init__.py +0 -0
  196. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/linear_scale/__pycache__/__init__.cpython-311.pyc +0 -0
  197. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/linear_scale/answering_instructions.jinja +0 -0
  198. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/linear_scale/question_presentation.jinja +0 -0
  199. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/list/__init__.py +0 -0
  200. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/list/__pycache__/__init__.cpython-311.pyc +0 -0
  201. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/list/answering_instructions.jinja +0 -0
  202. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/list/question_presentation.jinja +0 -0
  203. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/multiple_choice/__init__.py +0 -0
  204. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/multiple_choice/__pycache__/__init__.cpython-311.pyc +0 -0
  205. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/multiple_choice/answering_instructions.jinja +0 -0
  206. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/multiple_choice/html.jinja +0 -0
  207. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/multiple_choice/question_presentation.jinja +0 -0
  208. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/numerical/__init__.py +0 -0
  209. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/numerical/__pycache__/__init__.cpython-311.pyc +0 -0
  210. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/numerical/answering_instructions.jinja +0 -0
  211. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/numerical/question_presentation.jinja +0 -0
  212. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/rank/__init__.py +0 -0
  213. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/rank/__pycache__/__init__.cpython-311.pyc +0 -0
  214. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/rank/answering_instructions.jinja +0 -0
  215. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/rank/question_presentation.jinja +0 -0
  216. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/top_k/__init__.py +0 -0
  217. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/top_k/__pycache__/__init__.cpython-311.pyc +0 -0
  218. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/top_k/answering_instructions.jinja +0 -0
  219. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/top_k/question_presentation.jinja +0 -0
  220. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/yes_no/__init__.py +0 -0
  221. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/yes_no/__pycache__/__init__.cpython-311.pyc +0 -0
  222. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/yes_no/answering_instructions.jinja +0 -0
  223. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/questions/templates/yes_no/question_presentation.jinja +0 -0
  224. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/Dataset.py +0 -0
  225. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/DatasetExportMixin.py +0 -0
  226. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/DatasetTree.py +0 -0
  227. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/ResultsDBMixin.py +0 -0
  228. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/ResultsExportMixin.py +0 -0
  229. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/ResultsFetchMixin.py +0 -0
  230. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/ResultsGGMixin.py +0 -0
  231. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/ResultsToolsMixin.py +0 -0
  232. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/Selector.py +0 -0
  233. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/__init__.py +0 -0
  234. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/results/tree_explore.py +0 -0
  235. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/FileStore.py +0 -0
  236. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/Scenario.py +0 -0
  237. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/ScenarioHtmlMixin.py +0 -0
  238. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/ScenarioList.py +0 -0
  239. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/ScenarioListExportMixin.py +0 -0
  240. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/ScenarioListPdfMixin.py +0 -0
  241. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/scenarios/__init__.py +0 -0
  242. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/shared.py +0 -0
  243. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/study/ObjectEntry.py +0 -0
  244. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/study/ProofOfWork.py +0 -0
  245. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/study/SnapShot.py +0 -0
  246. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/study/Study.py +0 -0
  247. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/study/__init__.py +0 -0
  248. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/DAG.py +0 -0
  249. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/Memory.py +0 -0
  250. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/MemoryPlan.py +0 -0
  251. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/Rule.py +0 -0
  252. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/RuleCollection.py +0 -0
  253. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/Survey.py +0 -0
  254. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/SurveyCSS.py +0 -0
  255. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/SurveyExportMixin.py +0 -0
  256. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/SurveyFlowVisualizationMixin.py +0 -0
  257. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/SurveyQualtricsImport.py +0 -0
  258. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/__init__.py +0 -0
  259. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/base.py +0 -0
  260. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/descriptors.py +0 -0
  261. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/instructions/ChangeInstruction.py +0 -0
  262. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/instructions/Instruction.py +0 -0
  263. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/instructions/InstructionCollection.py +0 -0
  264. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/surveys/instructions/__init__.py +0 -0
  265. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/base.html +0 -0
  266. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/exceptions_by_model.html +0 -0
  267. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/exceptions_by_question_name.html +0 -0
  268. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/exceptions_by_type.html +0 -0
  269. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/interview_details.html +0 -0
  270. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/interviews.html +0 -0
  271. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/overview.html +0 -0
  272. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/performance_plot.html +0 -0
  273. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/report.css +0 -0
  274. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/report.html +0 -0
  275. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/templates/error_reporting/report.js +0 -0
  276. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/__init__.py +0 -0
  277. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/clusters.py +0 -0
  278. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/embeddings.py +0 -0
  279. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/embeddings_plotting.py +0 -0
  280. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/plotting.py +0 -0
  281. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/tools/summarize.py +0 -0
  282. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/SystemInfo.py +0 -0
  283. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/__init__.py +0 -0
  284. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/ast_utilities.py +0 -0
  285. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/data/Registry.py +0 -0
  286. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/data/__init__.py +0 -0
  287. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/data/scooter_results.json +0 -0
  288. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/decorators.py +0 -0
  289. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/gcp_bucket/__init__.py +0 -0
  290. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/gcp_bucket/cloud_storage.py +0 -0
  291. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/interface.py +0 -0
  292. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/repair_functions.py +0 -0
  293. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/restricted_python.py +0 -0
  294. {edsl-0.1.36.dev2 → edsl-0.1.36.dev4}/edsl/utilities/utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edsl
3
- Version: 0.1.36.dev2
3
+ Version: 0.1.36.dev4
4
4
  Summary: Create and analyze LLM-based surveys
5
5
  Home-page: https://www.expectedparrot.com/
6
6
  License: MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.1.36.dev4"
@@ -16,30 +16,26 @@ class PriceFetcher:
16
16
  if self._cached_prices is not None:
17
17
  return self._cached_prices
18
18
 
19
+ import os
19
20
  import requests
20
- import csv
21
- from io import StringIO
22
-
23
- sheet_id = "1SAO3Bhntefl0XQHJv27rMxpvu6uzKDWNXFHRa7jrUDs"
24
-
25
- # Construct the URL to fetch the CSV
26
- url = f"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=csv"
21
+ from edsl import CONFIG
27
22
 
28
23
  try:
29
- # Fetch the CSV data
30
- response = requests.get(url)
24
+ # Fetch the pricing data
25
+ url = f"{CONFIG.EXPECTED_PARROT_URL}/api/v0/prices"
26
+ api_key = os.getenv("EXPECTED_PARROT_API_KEY")
27
+ headers = {}
28
+ if api_key:
29
+ headers["Authorization"] = f"Bearer {api_key}"
30
+ else:
31
+ headers["Authorization"] = f"Bearer None"
32
+
33
+ response = requests.get(url, headers=headers, timeout=20)
31
34
  response.raise_for_status() # Raise an exception for bad responses
32
35
 
33
- # Parse the CSV data
34
- csv_data = StringIO(response.text)
35
- reader = csv.reader(csv_data)
36
-
37
- # Convert to list of dictionaries
38
- headers = next(reader)
39
- data = [dict(zip(headers, row)) for row in reader]
36
+ # Parse the data
37
+ data = response.json()
40
38
 
41
- # self._cached_prices = data
42
- # return data
43
39
  price_lookup = {}
44
40
  for entry in data:
45
41
  service = entry.get("service", None)
@@ -6,6 +6,7 @@ from typing import Any, Optional, Union, Literal
6
6
  from uuid import UUID
7
7
  import edsl
8
8
  from edsl import CONFIG, CacheEntry, Jobs, Survey
9
+ from edsl.exceptions.coop import CoopNoUUIDError, CoopServerResponseError
9
10
  from edsl.coop.utils import (
10
11
  EDSLObject,
11
12
  ObjectRegistry,
@@ -99,7 +100,7 @@ class Coop:
99
100
  if "Authorization" in message:
100
101
  print(message)
101
102
  message = "Please provide an Expected Parrot API key."
102
- raise Exception(message)
103
+ raise CoopServerResponseError(message)
103
104
 
104
105
  def _json_handle_none(self, value: Any) -> Any:
105
106
  """
@@ -116,7 +117,7 @@ class Coop:
116
117
  Resolve the uuid from a uuid or a url.
117
118
  """
118
119
  if not url and not uuid:
119
- raise Exception("No uuid or url provided for the object.")
120
+ raise CoopNoUUIDError("No uuid or url provided for the object.")
120
121
  if not uuid and url:
121
122
  uuid = url.split("/")[-1]
122
123
  return uuid
@@ -584,7 +585,10 @@ class Coop:
584
585
  )
585
586
  self._resolve_server_response(response)
586
587
  response_json = response.json()
587
- return response_json.get("cost")
588
+ return {
589
+ "credits": response_json.get("cost_in_credits"),
590
+ "usd": response_json.get("cost_in_usd"),
591
+ }
588
592
 
589
593
  ################
590
594
  # Remote Errors
@@ -0,0 +1,84 @@
1
+ class RemoteCacheSync:
2
+ def __init__(self, coop, cache, output_func, remote_cache=True, remote_cache_description=""):
3
+ self.coop = coop
4
+ self.cache = cache
5
+ self._output = output_func
6
+ self.remote_cache = remote_cache
7
+ self.old_entry_keys = []
8
+ self.new_cache_entries = []
9
+ self.remote_cache_description = remote_cache_description
10
+
11
+ def __enter__(self):
12
+ if self.remote_cache:
13
+ self._sync_from_remote()
14
+ self.old_entry_keys = list(self.cache.keys())
15
+ return self
16
+
17
+ def __exit__(self, exc_type, exc_value, traceback):
18
+ if self.remote_cache:
19
+ self._sync_to_remote()
20
+ return False # Propagate exceptions
21
+
22
+ def _sync_from_remote(self):
23
+ cache_difference = self.coop.remote_cache_get_diff(self.cache.keys())
24
+ client_missing_cacheentries = cache_difference.get("client_missing_cacheentries", [])
25
+ missing_entry_count = len(client_missing_cacheentries)
26
+
27
+ if missing_entry_count > 0:
28
+ self._output(
29
+ f"Updating local cache with {missing_entry_count:,} new "
30
+ f"{'entry' if missing_entry_count == 1 else 'entries'} from remote..."
31
+ )
32
+ self.cache.add_from_dict({entry.key: entry for entry in client_missing_cacheentries})
33
+ self._output("Local cache updated!")
34
+ else:
35
+ self._output("No new entries to add to local cache.")
36
+
37
+ def _sync_to_remote(self):
38
+ cache_difference = self.coop.remote_cache_get_diff(self.cache.keys())
39
+ server_missing_cacheentry_keys = cache_difference.get("server_missing_cacheentry_keys", [])
40
+ server_missing_cacheentries = [
41
+ entry
42
+ for key in server_missing_cacheentry_keys
43
+ if (entry := self.cache.data.get(key)) is not None
44
+ ]
45
+
46
+ new_cache_entries = [
47
+ entry for entry in self.cache.values() if entry.key not in self.old_entry_keys
48
+ ]
49
+ server_missing_cacheentries.extend(new_cache_entries)
50
+ new_entry_count = len(server_missing_cacheentries)
51
+
52
+ if new_entry_count > 0:
53
+ self._output(
54
+ f"Updating remote cache with {new_entry_count:,} new "
55
+ f"{'entry' if new_entry_count == 1 else 'entries'}..."
56
+ )
57
+ self.coop.remote_cache_create_many(
58
+ server_missing_cacheentries,
59
+ visibility="private",
60
+ description=self.remote_cache_description,
61
+ )
62
+ self._output("Remote cache updated!")
63
+ else:
64
+ self._output("No new entries to add to remote cache.")
65
+
66
+ self._output(f"There are {len(self.cache.keys()):,} entries in the local cache.")
67
+
68
+ # # Usage example
69
+ # def run_job(self, n, progress_bar, cache, stop_on_exception, sidecar_model, print_exceptions, raise_validation_errors, use_remote_cache=True):
70
+ # with RemoteCacheSync(self.coop, cache, self._output, remote_cache=use_remote_cache):
71
+ # self._output("Running job...")
72
+ # results = self._run_local(
73
+ # n=n,
74
+ # progress_bar=progress_bar,
75
+ # cache=cache,
76
+ # stop_on_exception=stop_on_exception,
77
+ # sidecar_model=sidecar_model,
78
+ # print_exceptions=print_exceptions,
79
+ # raise_validation_errors=raise_validation_errors,
80
+ # )
81
+ # self._output("Job completed!")
82
+
83
+ # results.cache = cache.new_entries_cache()
84
+ # return results
@@ -0,0 +1,10 @@
1
+ class CoopErrors(Exception):
2
+ pass
3
+
4
+
5
+ class CoopNoUUIDError(CoopErrors):
6
+ pass
7
+
8
+
9
+ class CoopServerResponseError(CoopErrors):
10
+ pass
@@ -1,8 +1,10 @@
1
1
  # """The Jobs class is a collection of agents, scenarios and models and one survey."""
2
2
  from __future__ import annotations
3
3
  import warnings
4
+ import requests
4
5
  from itertools import product
5
6
  from typing import Optional, Union, Sequence, Generator
7
+
6
8
  from edsl.Base import Base
7
9
  from edsl.exceptions import MissingAPIKeyError
8
10
  from edsl.jobs.buckets.BucketCollection import BucketCollection
@@ -10,6 +12,9 @@ from edsl.jobs.interviews.Interview import Interview
10
12
  from edsl.jobs.runners.JobsRunnerAsyncio import JobsRunnerAsyncio
11
13
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
12
14
 
15
+ from edsl.data.RemoteCacheSync import RemoteCacheSync
16
+ from edsl.exceptions.coop import CoopServerResponseError
17
+
13
18
 
14
19
  class Jobs(Base):
15
20
  """
@@ -203,10 +208,6 @@ class Jobs(Base):
203
208
  ]
204
209
  )
205
210
  return d
206
- # if table:
207
- # d.to_scenario_list().print(format="rich")
208
- # else:
209
- # return d
210
211
 
211
212
  def show_prompts(self) -> None:
212
213
  """Print the prompts."""
@@ -219,11 +220,11 @@ class Jobs(Base):
219
220
  price_lookup: dict,
220
221
  inference_service: str,
221
222
  model: str,
222
- ):
223
+ ) -> dict:
223
224
  """Estimates the cost of a prompt. Takes piping into account."""
224
225
 
225
226
  def get_piping_multiplier(prompt: str):
226
- """Returns 2 if a prompt includes Jinja brances, and 1 otherwise."""
227
+ """Returns 2 if a prompt includes Jinja braces, and 1 otherwise."""
227
228
 
228
229
  if "{{" in prompt and "}}" in prompt:
229
230
  return 2
@@ -231,9 +232,25 @@ class Jobs(Base):
231
232
 
232
233
  # Look up prices per token
233
234
  key = (inference_service, model)
234
- relevant_prices = price_lookup[key]
235
- output_price_per_token = 1 / float(relevant_prices["output"]["one_usd_buys"])
236
- input_price_per_token = 1 / float(relevant_prices["input"]["one_usd_buys"])
235
+
236
+ try:
237
+ relevant_prices = price_lookup[key]
238
+ output_price_per_token = 1 / float(
239
+ relevant_prices["output"]["one_usd_buys"]
240
+ )
241
+ input_price_per_token = 1 / float(relevant_prices["input"]["one_usd_buys"])
242
+ except KeyError:
243
+ # A KeyError is likely to occur if we cannot retrieve prices (the price_lookup dict is empty)
244
+ # Use a sensible default
245
+
246
+ import warnings
247
+
248
+ warnings.warn(
249
+ "Price data could not be retrieved. Using default estimates for input and output token prices. Input: $0.15 / 1M tokens; Output: $0.60 / 1M tokens"
250
+ )
251
+
252
+ output_price_per_token = 0.00000015 # $0.15 / 1M tokens
253
+ input_price_per_token = 0.00000060 # $0.60 / 1M tokens
237
254
 
238
255
  # Compute the number of characters (double if the question involves piping)
239
256
  user_prompt_chars = len(str(user_prompt)) * get_piping_multiplier(
@@ -258,7 +275,7 @@ class Jobs(Base):
258
275
  "cost": cost,
259
276
  }
260
277
 
261
- def estimate_job_cost_from_external_prices(self, price_lookup: dict):
278
+ def estimate_job_cost_from_external_prices(self, price_lookup: dict) -> dict:
262
279
  """
263
280
  Estimates the cost of a job according to the following assumptions:
264
281
 
@@ -341,7 +358,7 @@ class Jobs(Base):
341
358
 
342
359
  return output
343
360
 
344
- def estimate_job_cost(self):
361
+ def estimate_job_cost(self) -> dict:
345
362
  """
346
363
  Estimates the cost of a job according to the following assumptions:
347
364
 
@@ -357,6 +374,25 @@ class Jobs(Base):
357
374
 
358
375
  return self.estimate_job_cost_from_external_prices(price_lookup=price_lookup)
359
376
 
377
+ @staticmethod
378
+ def compute_job_cost(job_results: "Results") -> float:
379
+ """
380
+ Computes the cost of a completed job in USD.
381
+ """
382
+ total_cost = 0
383
+ for result in job_results:
384
+ for key in result.raw_model_response:
385
+ if key.endswith("_cost"):
386
+ result_cost = result.raw_model_response[key]
387
+
388
+ question_name = key.removesuffix("_cost")
389
+ cache_used = result.cache_used_dict[question_name]
390
+
391
+ if isinstance(result_cost, (int, float)) and not cache_used:
392
+ total_cost += result_cost
393
+
394
+ return total_cost
395
+
360
396
  @staticmethod
361
397
  def _get_container_class(object):
362
398
  from edsl.agents.AgentList import AgentList
@@ -580,7 +616,7 @@ class Jobs(Base):
580
616
 
581
617
  def _output(self, message) -> None:
582
618
  """Check if a Job is verbose. If so, print the message."""
583
- if self.verbose:
619
+ if hasattr(self, "verbose") and self.verbose:
584
620
  print(message)
585
621
 
586
622
  def _check_parameters(self, strict=False, warn=False) -> None:
@@ -657,6 +693,122 @@ class Jobs(Base):
657
693
  return False
658
694
  return self._raise_validation_errors
659
695
 
696
+ def create_remote_inference_job(
697
+ self, iterations: int = 1, remote_inference_description: Optional[str] = None
698
+ ):
699
+ """ """
700
+ from edsl.coop.coop import Coop
701
+
702
+ coop = Coop()
703
+ self._output("Remote inference activated. Sending job to server...")
704
+ remote_job_creation_data = coop.remote_inference_create(
705
+ self,
706
+ description=remote_inference_description,
707
+ status="queued",
708
+ iterations=iterations,
709
+ )
710
+ return remote_job_creation_data
711
+
712
+ @staticmethod
713
+ def check_status(job_uuid):
714
+ from edsl.coop.coop import Coop
715
+
716
+ coop = Coop()
717
+ return coop.remote_inference_get(job_uuid)
718
+
719
+ def poll_remote_inference_job(
720
+ self, remote_job_creation_data: dict
721
+ ) -> Union[Results, None]:
722
+ from edsl.coop.coop import Coop
723
+ import time
724
+ from datetime import datetime
725
+ from edsl.config import CONFIG
726
+
727
+ expected_parrot_url = CONFIG.get("EXPECTED_PARROT_URL")
728
+
729
+ job_uuid = remote_job_creation_data.get("uuid")
730
+
731
+ coop = Coop()
732
+ job_in_queue = True
733
+ while job_in_queue:
734
+ remote_job_data = coop.remote_inference_get(job_uuid)
735
+ status = remote_job_data.get("status")
736
+ if status == "cancelled":
737
+ print("\r" + " " * 80 + "\r", end="")
738
+ print("Job cancelled by the user.")
739
+ print(
740
+ f"See {expected_parrot_url}/home/remote-inference for more details."
741
+ )
742
+ return None
743
+ elif status == "failed":
744
+ print("\r" + " " * 80 + "\r", end="")
745
+ print("Job failed.")
746
+ print(
747
+ f"See {expected_parrot_url}/home/remote-inference for more details."
748
+ )
749
+ return None
750
+ elif status == "completed":
751
+ results_uuid = remote_job_data.get("results_uuid")
752
+ results = coop.get(results_uuid, expected_object_type="results")
753
+ print("\r" + " " * 80 + "\r", end="")
754
+ print(
755
+ f"Job completed and Results stored on Coop (Results uuid={results_uuid})."
756
+ )
757
+ return results
758
+ else:
759
+ duration = 5
760
+ time_checked = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
761
+ frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
762
+ start_time = time.time()
763
+ i = 0
764
+ while time.time() - start_time < duration:
765
+ print(
766
+ f"\r{frames[i % len(frames)]} Job status: {status} - last update: {time_checked}",
767
+ end="",
768
+ flush=True,
769
+ )
770
+ time.sleep(0.1)
771
+ i += 1
772
+
773
+ def use_remote_inference(self, disable_remote_inference: bool):
774
+ if disable_remote_inference:
775
+ return False
776
+ if not disable_remote_inference:
777
+ try:
778
+ from edsl import Coop
779
+
780
+ user_edsl_settings = Coop().edsl_settings
781
+ return user_edsl_settings.get("remote_inference", False)
782
+ except requests.ConnectionError:
783
+ pass
784
+ except CoopServerResponseError as e:
785
+ pass
786
+
787
+ return False
788
+
789
+ def use_remote_cache(self):
790
+ try:
791
+ from edsl import Coop
792
+
793
+ user_edsl_settings = Coop().edsl_settings
794
+ return user_edsl_settings.get("remote_caching", False)
795
+ except requests.ConnectionError:
796
+ pass
797
+ except CoopServerResponseError as e:
798
+ pass
799
+
800
+ return False
801
+
802
+ def check_api_keys(self):
803
+ from edsl import Model
804
+
805
+ for model in self.models + [Model()]:
806
+ if not model.has_valid_api_key():
807
+ raise MissingAPIKeyError(
808
+ model_name=str(model.model),
809
+ inference_service=model._inference_service_,
810
+ )
811
+
660
812
  def run(
661
813
  self,
662
814
  n: int = 1,
@@ -694,91 +846,17 @@ class Jobs(Base):
694
846
 
695
847
  self.verbose = verbose
696
848
 
697
- remote_cache = False
698
- remote_inference = False
699
-
700
- if not disable_remote_inference:
701
- try:
702
- coop = Coop()
703
- user_edsl_settings = Coop().edsl_settings
704
- remote_cache = user_edsl_settings.get("remote_caching", False)
705
- remote_inference = user_edsl_settings.get("remote_inference", False)
706
- except Exception:
707
- pass
708
-
709
- if remote_inference:
710
- import time
711
- from datetime import datetime
712
- from edsl.config import CONFIG
713
-
714
- expected_parrot_url = CONFIG.get("EXPECTED_PARROT_URL")
715
-
716
- self._output("Remote inference activated. Sending job to server...")
717
- if remote_cache:
718
- self._output(
719
- "Remote caching activated. The remote cache will be used for this job."
720
- )
721
-
722
- remote_job_creation_data = coop.remote_inference_create(
723
- self,
724
- description=remote_inference_description,
725
- status="queued",
726
- iterations=n,
849
+ if remote_inference := self.use_remote_inference(disable_remote_inference):
850
+ remote_job_creation_data = self.create_remote_inference_job(
851
+ iterations=n, remote_inference_description=remote_inference_description
727
852
  )
728
- time_queued = datetime.now().strftime("%m/%d/%Y %I:%M:%S %p")
729
- job_uuid = remote_job_creation_data.get("uuid")
730
- print(f"Remote inference started (Job uuid={job_uuid}).")
731
- # print(f"Job queued at {time_queued}.")
732
- job_in_queue = True
733
- while job_in_queue:
734
- remote_job_data = coop.remote_inference_get(job_uuid)
735
- status = remote_job_data.get("status")
736
- if status == "cancelled":
737
- print("\r" + " " * 80 + "\r", end="")
738
- print("Job cancelled by the user.")
739
- print(
740
- f"See {expected_parrot_url}/home/remote-inference for more details."
741
- )
742
- return None
743
- elif status == "failed":
744
- print("\r" + " " * 80 + "\r", end="")
745
- print("Job failed.")
746
- print(
747
- f"See {expected_parrot_url}/home/remote-inference for more details."
748
- )
749
- return None
750
- elif status == "completed":
751
- results_uuid = remote_job_data.get("results_uuid")
752
- results = coop.get(results_uuid, expected_object_type="results")
753
- print("\r" + " " * 80 + "\r", end="")
754
- print(
755
- f"Job completed and Results stored on Coop (Results uuid={results_uuid})."
756
- )
757
- return results
758
- else:
759
- duration = 5
760
- time_checked = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
761
- frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
762
- start_time = time.time()
763
- i = 0
764
- while time.time() - start_time < duration:
765
- print(
766
- f"\r{frames[i % len(frames)]} Job status: {status} - last update: {time_checked}",
767
- end="",
768
- flush=True,
769
- )
770
- time.sleep(0.1)
771
- i += 1
772
- else:
773
- if check_api_keys:
774
- from edsl import Model
853
+ results = self.poll_remote_inference_job(remote_job_creation_data)
854
+ if results is None:
855
+ self._output("Job failed.")
856
+ return results
775
857
 
776
- for model in self.models + [Model()]:
777
- if not model.has_valid_api_key():
778
- raise MissingAPIKeyError(
779
- model_name=str(model.model),
780
- inference_service=model._inference_service_,
781
- )
858
+ if check_api_keys:
859
+ self.check_api_keys()
782
860
 
783
861
  # handle cache
784
862
  if cache is None or cache is True:
@@ -790,51 +868,14 @@ class Jobs(Base):
790
868
 
791
869
  cache = Cache()
792
870
 
793
- if not remote_cache:
794
- results = self._run_local(
795
- n=n,
796
- progress_bar=progress_bar,
797
- cache=cache,
798
- stop_on_exception=stop_on_exception,
799
- sidecar_model=sidecar_model,
800
- print_exceptions=print_exceptions,
801
- raise_validation_errors=raise_validation_errors,
802
- )
803
-
804
- results.cache = cache.new_entries_cache()
805
-
806
- self._output(f"There are {len(cache.keys()):,} entries in the local cache.")
807
- else:
808
- cache_difference = coop.remote_cache_get_diff(cache.keys())
809
-
810
- client_missing_cacheentries = cache_difference.get(
811
- "client_missing_cacheentries", []
812
- )
813
-
814
- missing_entry_count = len(client_missing_cacheentries)
815
- if missing_entry_count > 0:
816
- self._output(
817
- f"Updating local cache with {missing_entry_count:,} new "
818
- f"{'entry' if missing_entry_count == 1 else 'entries'} from remote..."
819
- )
820
- cache.add_from_dict(
821
- {entry.key: entry for entry in client_missing_cacheentries}
822
- )
823
- self._output("Local cache updated!")
824
- else:
825
- self._output("No new entries to add to local cache.")
826
-
827
- server_missing_cacheentry_keys = cache_difference.get(
828
- "server_missing_cacheentry_keys", []
829
- )
830
- server_missing_cacheentries = [
831
- entry
832
- for key in server_missing_cacheentry_keys
833
- if (entry := cache.data.get(key)) is not None
834
- ]
835
- old_entry_keys = [key for key in cache.keys()]
836
-
837
- self._output("Running job...")
871
+ remote_cache = self.use_remote_cache()
872
+ with RemoteCacheSync(
873
+ coop=Coop(),
874
+ cache=cache,
875
+ output_func=self._output,
876
+ remote_cache=remote_cache,
877
+ remote_cache_description=remote_cache_description,
878
+ ) as r:
838
879
  results = self._run_local(
839
880
  n=n,
840
881
  progress_bar=progress_bar,
@@ -844,32 +885,8 @@ class Jobs(Base):
844
885
  print_exceptions=print_exceptions,
845
886
  raise_validation_errors=raise_validation_errors,
846
887
  )
847
- self._output("Job completed!")
848
-
849
- new_cache_entries = list(
850
- [entry for entry in cache.values() if entry.key not in old_entry_keys]
851
- )
852
- server_missing_cacheentries.extend(new_cache_entries)
853
-
854
- new_entry_count = len(server_missing_cacheentries)
855
- if new_entry_count > 0:
856
- self._output(
857
- f"Updating remote cache with {new_entry_count:,} new "
858
- f"{'entry' if new_entry_count == 1 else 'entries'}..."
859
- )
860
- coop.remote_cache_create_many(
861
- server_missing_cacheentries,
862
- visibility="private",
863
- description=remote_cache_description,
864
- )
865
- self._output("Remote cache updated!")
866
- else:
867
- self._output("No new entries to add to remote cache.")
868
-
869
- results.cache = cache.new_entries_cache()
870
-
871
- self._output(f"There are {len(cache.keys()):,} entries in the local cache.")
872
888
 
889
+ results.cache = cache.new_entries_cache()
873
890
  return results
874
891
 
875
892
  def _run_local(self, *args, **kwargs):
@@ -159,13 +159,13 @@ class Interview:
159
159
  return self.task_creators.interview_status
160
160
 
161
161
  # region: Serialization
162
- def _to_dict(self, include_exceptions=False) -> dict[str, Any]:
162
+ def _to_dict(self, include_exceptions=True) -> dict[str, Any]:
163
163
  """Return a dictionary representation of the Interview instance.
164
164
  This is just for hashing purposes.
165
165
 
166
166
  >>> i = Interview.example()
167
167
  >>> hash(i)
168
- 1646262796627658719
168
+ 1217840301076717434
169
169
  """
170
170
  d = {
171
171
  "agent": self.agent._to_dict(),
@@ -177,11 +177,29 @@ class Interview:
177
177
  }
178
178
  if include_exceptions:
179
179
  d["exceptions"] = self.exceptions.to_dict()
180
+ return d
181
+
182
+ @classmethod
183
+ def from_dict(cls, d: dict[str, Any]) -> "Interview":
184
+ """Return an Interview instance from a dictionary."""
185
+ agent = Agent.from_dict(d["agent"])
186
+ survey = Survey.from_dict(d["survey"])
187
+ scenario = Scenario.from_dict(d["scenario"])
188
+ model = LanguageModel.from_dict(d["model"])
189
+ iteration = d["iteration"]
190
+ return cls(agent=agent, survey=survey, scenario=scenario, model=model, iteration=iteration)
180
191
 
181
192
  def __hash__(self) -> int:
182
193
  from edsl.utilities.utilities import dict_hash
183
194
 
184
- return dict_hash(self._to_dict())
195
+ return dict_hash(self._to_dict(include_exceptions=False))
196
+
197
+ def __eq__(self, other: "Interview") -> bool:
198
+ """
199
+ >>> from edsl.jobs.interviews.Interview import Interview; i = Interview.example(); d = i._to_dict(); i2 = Interview.from_dict(d); i == i2
200
+ True
201
+ """
202
+ return hash(self) == hash(other)
185
203
 
186
204
  # endregion
187
205