edsl 0.1.47__py3-none-any.whl → 0.1.49__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 +311 -75
  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.49.dist-info}/METADATA +1 -1
  230. edsl-0.1.49.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.49.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/WHEEL +0 -0
edsl/coop/coop.py CHANGED
@@ -2,22 +2,24 @@ import aiohttp
2
2
  import json
3
3
  import requests
4
4
 
5
- from typing import Any, Optional, Union, Literal, TypedDict
5
+ from typing import Any, Optional, Union, Literal, TypedDict, TYPE_CHECKING
6
6
  from uuid import UUID
7
7
 
8
- import edsl
8
+ from .. import __version__
9
9
 
10
- from edsl.config import CONFIG
11
- from edsl.data.CacheEntry import CacheEntry
12
- from edsl.jobs.Jobs import Jobs
13
- from edsl.surveys.Survey import Survey
10
+ from ..config import CONFIG
11
+ from ..caching import CacheEntry
14
12
 
15
- from edsl.exceptions.coop import (
13
+ if TYPE_CHECKING:
14
+ from ..jobs import Jobs
15
+ from ..surveys import Survey
16
+
17
+ from .exceptions import (
16
18
  CoopInvalidURLError,
17
19
  CoopNoUUIDError,
18
20
  CoopServerResponseError,
19
21
  )
20
- from edsl.coop.utils import (
22
+ from .utils import (
21
23
  EDSLObject,
22
24
  ObjectRegistry,
23
25
  ObjectType,
@@ -25,10 +27,10 @@ from edsl.coop.utils import (
25
27
  VisibilityType,
26
28
  )
27
29
 
28
- from edsl.coop.CoopFunctionsMixin import CoopFunctionsMixin
29
- from edsl.coop.ExpectedParrotKeyHandler import ExpectedParrotKeyHandler
30
+ from .coop_functions import CoopFunctionsMixin
31
+ from .ep_key_handling import ExpectedParrotKeyHandler
30
32
 
31
- from edsl.inference_services.data_structures import ServiceToModelsMapping
33
+ from ..inference_services.data_structures import ServiceToModelsMapping
32
34
 
33
35
 
34
36
  class RemoteInferenceResponse(TypedDict):
@@ -54,16 +56,58 @@ class RemoteInferenceCreationInfo(TypedDict):
54
56
 
55
57
  class Coop(CoopFunctionsMixin):
56
58
  """
57
- Client for the Expected Parrot API.
59
+ Client for the Expected Parrot API that provides cloud-based functionality for EDSL.
60
+
61
+ The Coop class is the main interface for interacting with Expected Parrot's cloud services.
62
+ It enables:
63
+
64
+ 1. Storing and retrieving EDSL objects (surveys, agents, models, results, etc.)
65
+ 2. Running inference jobs remotely for better performance and scalability
66
+ 3. Retrieving and caching interview results
67
+ 4. Managing API keys and authentication
68
+ 5. Accessing model availability and pricing information
69
+
70
+ The client handles authentication, serialization/deserialization of EDSL objects,
71
+ and communication with the Expected Parrot API endpoints. It also provides
72
+ methods for tracking job status and managing results.
73
+
74
+ When initialized without parameters, Coop will attempt to use an API key from:
75
+ 1. The EXPECTED_PARROT_API_KEY environment variable
76
+ 2. A stored key in the user's config directory
77
+ 3. Interactive login if needed
78
+
79
+ Attributes:
80
+ api_key (str): The API key used for authentication
81
+ url (str): The base URL for the Expected Parrot API
82
+ api_url (str): The URL for API endpoints (derived from base URL)
58
83
  """
59
84
 
60
85
  def __init__(
61
86
  self, api_key: Optional[str] = None, url: Optional[str] = None
62
87
  ) -> None:
63
88
  """
64
- Initialize the client.
65
- - Provide an API key directly, or through an env variable.
66
- - Provide a URL directly, or use the default one.
89
+ Initialize the Expected Parrot API client.
90
+
91
+ This constructor sets up the connection to Expected Parrot's cloud services.
92
+ If not provided explicitly, it will attempt to obtain an API key from
93
+ environment variables or from a stored location in the user's config directory.
94
+
95
+ Parameters:
96
+ api_key (str, optional): API key for authentication with Expected Parrot.
97
+ If not provided, will attempt to obtain from environment or stored location.
98
+ url (str, optional): Base URL for the Expected Parrot service.
99
+ If not provided, uses the default from configuration.
100
+
101
+ Notes:
102
+ - The API key is stored in the EXPECTED_PARROT_API_KEY environment variable
103
+ or in a platform-specific config directory
104
+ - The URL is determined based on whether it's a production, staging,
105
+ or development environment
106
+ - The api_url for actual API endpoints is derived from the base URL
107
+
108
+ Example:
109
+ >>> coop = Coop() # Uses API key from environment or stored location
110
+ >>> coop = Coop(api_key="your-api-key") # Explicitly provide API key
67
111
  """
68
112
  self.ep_key_handler = ExpectedParrotKeyHandler()
69
113
  self.api_key = api_key or self.ep_key_handler.get_ep_api_key()
@@ -79,7 +123,7 @@ class Coop(CoopFunctionsMixin):
79
123
  self.api_url = "http://localhost:8000"
80
124
  else:
81
125
  self.api_url = self.url
82
- self._edsl_version = edsl.__version__
126
+ self._edsl_version = __version__
83
127
 
84
128
  def get_progress_bar_url(self):
85
129
  return f"{CONFIG.EXPECTED_PARROT_URL}"
@@ -184,14 +228,14 @@ class Coop(CoopFunctionsMixin):
184
228
  # breakpoint()
185
229
  server_edsl_version = response.headers.get("X-EDSL-Version")
186
230
 
187
- if server_edsl_version:
188
- if self._user_version_is_outdated(
189
- user_version_str=self._edsl_version,
190
- server_version_str=server_edsl_version,
191
- ):
192
- print(
193
- "Please upgrade your EDSL version to access our latest features. Open your terminal and run `pip install --upgrade edsl`"
194
- )
231
+ # if server_edsl_version:
232
+ # if self._user_version_is_outdated(
233
+ # user_version_str=self._edsl_version,
234
+ # server_version_str=server_edsl_version,
235
+ # ):
236
+ # print(
237
+ # "Please upgrade your EDSL version to access our latest features. Open your terminal and run `pip install --upgrade edsl`"
238
+ # )
195
239
 
196
240
  if response.status_code >= 400:
197
241
  try:
@@ -205,7 +249,7 @@ class Coop(CoopFunctionsMixin):
205
249
  # print(response.text)
206
250
  if "The API key you provided is invalid" in message and check_api_key:
207
251
  import secrets
208
- from edsl.utilities.utilities import write_api_key_to_env
252
+ from ..utilities.utilities import write_api_key_to_env
209
253
 
210
254
  edsl_auth_token = secrets.token_urlsafe(16)
211
255
 
@@ -239,6 +283,30 @@ class Coop(CoopFunctionsMixin):
239
283
 
240
284
  raise CoopServerResponseError(message)
241
285
 
286
+ def _resolve_gcs_response(self, response: requests.Response) -> None:
287
+ """
288
+ Check the response from uploading or downloading a file from Google Cloud Storage.
289
+ Raise errors as appropriate.
290
+ """
291
+ if response.status_code >= 400:
292
+ try:
293
+ import xml.etree.ElementTree as ET
294
+
295
+ # Extract elements from XML string
296
+ root = ET.fromstring(response.text)
297
+
298
+ code = root.find("Code").text
299
+ message = root.find("Message").text
300
+ details = root.find("Details").text
301
+ except Exception:
302
+ raise Exception(
303
+ f"Server returned status code {response.status_code}",
304
+ "XML response could not be decoded.",
305
+ "The server response was: " + response.text,
306
+ )
307
+
308
+ raise Exception(f"An error occurred: {code} - {message} - {details}")
309
+
242
310
  def _poll_for_api_key(
243
311
  self, edsl_auth_token: str, timeout: int = 120
244
312
  ) -> Union[str, None]:
@@ -372,7 +440,35 @@ class Coop(CoopFunctionsMixin):
372
440
  visibility: Optional[VisibilityType] = "unlisted",
373
441
  ) -> dict:
374
442
  """
375
- Create an EDSL object in the Coop server.
443
+ Store an EDSL object in the Expected Parrot cloud service.
444
+
445
+ This method uploads an EDSL object (like a Survey, Agent, or Results) to the
446
+ Expected Parrot cloud service for storage, sharing, or further processing.
447
+
448
+ Parameters:
449
+ object (EDSLObject): The EDSL object to store (Survey, Agent, Results, etc.)
450
+ description (str, optional): A human-readable description of the object
451
+ alias (str, optional): A custom alias for easier reference later
452
+ visibility (VisibilityType, optional): Access level for the object. One of:
453
+ - "private": Only accessible by the owner
454
+ - "public": Accessible by anyone
455
+ - "unlisted": Accessible with the link, but not listed publicly
456
+
457
+ Returns:
458
+ dict: Information about the created object including:
459
+ - url: The URL to access the object
460
+ - alias_url: The URL with the custom alias (if provided)
461
+ - uuid: The unique identifier for the object
462
+ - visibility: The visibility setting
463
+ - version: The EDSL version used to create the object
464
+
465
+ Raises:
466
+ CoopServerResponseError: If there's an error communicating with the server
467
+
468
+ Example:
469
+ >>> survey = Survey(questions=[QuestionFreeText(question_name="name")])
470
+ >>> result = coop.create(survey, description="Basic survey", visibility="public")
471
+ >>> print(result["url"]) # URL to access the survey
376
472
  """
377
473
  object_type = ObjectRegistry.get_object_type_by_edsl_class(object)
378
474
  response = self._send_server_request(
@@ -381,12 +477,14 @@ class Coop(CoopFunctionsMixin):
381
477
  payload={
382
478
  "description": description,
383
479
  "alias": alias,
384
- "json_string": json.dumps(
385
- object.to_dict(),
386
- default=self._json_handle_none,
387
- )
388
- if object_type != "scenario"
389
- else "",
480
+ "json_string": (
481
+ json.dumps(
482
+ object.to_dict(),
483
+ default=self._json_handle_none,
484
+ )
485
+ if object_type != "scenario"
486
+ else ""
487
+ ),
390
488
  "object_type": object_type,
391
489
  "visibility": visibility,
392
490
  "version": self._edsl_version,
@@ -409,6 +507,7 @@ class Coop(CoopFunctionsMixin):
409
507
  response = requests.put(
410
508
  signed_url, data=json_data.encode(), headers=headers
411
509
  )
510
+ self._resolve_gcs_response(response)
412
511
  owner_username = response_json.get("owner_username")
413
512
  object_alias = response_json.get("alias")
414
513
 
@@ -429,15 +528,39 @@ class Coop(CoopFunctionsMixin):
429
528
  expected_object_type: Optional[ObjectType] = None,
430
529
  ) -> EDSLObject:
431
530
  """
432
- Retrieve an EDSL object by its uuid/url or by owner username and alias.
433
- - If the object's visibility is private, the user must be the owner.
434
- - Optionally, check if the retrieved object is of a certain type.
531
+ Retrieve an EDSL object from the Expected Parrot cloud service.
435
532
 
436
- :param url_or_uuid: The UUID or URL of the object.
437
- URLs can be in the form content/uuid or content/username/alias.
438
- :param expected_object_type: The expected type of the object.
533
+ This method downloads and deserializes an EDSL object from the cloud service
534
+ using either its UUID, URL, or username/alias combination.
535
+
536
+ Parameters:
537
+ url_or_uuid (Union[str, UUID]): Identifier for the object to retrieve.
538
+ Can be one of:
539
+ - UUID string (e.g., "123e4567-e89b-12d3-a456-426614174000")
540
+ - Full URL (e.g., "https://expectedparrot.com/content/123e4567...")
541
+ - Alias URL (e.g., "https://expectedparrot.com/content/username/my-survey")
542
+ expected_object_type (ObjectType, optional): If provided, validates that the
543
+ retrieved object is of the expected type (e.g., "survey", "agent")
439
544
 
440
- :return: the object instance.
545
+ Returns:
546
+ EDSLObject: The retrieved object as its original EDSL class instance
547
+ (e.g., Survey, Agent, Results)
548
+
549
+ Raises:
550
+ CoopNoUUIDError: If no UUID or URL is provided
551
+ CoopInvalidURLError: If the URL format is invalid
552
+ CoopServerResponseError: If the server returns an error (e.g., not found,
553
+ unauthorized access)
554
+ Exception: If the retrieved object doesn't match the expected type
555
+
556
+ Notes:
557
+ - If the object's visibility is set to "private", you must be the owner to access it
558
+ - For objects stored with an alias, you can use either the UUID or the alias URL
559
+
560
+ Example:
561
+ >>> survey = coop.get("123e4567-e89b-12d3-a456-426614174000")
562
+ >>> survey = coop.get("https://expectedparrot.com/content/username/my-survey")
563
+ >>> survey = coop.get(url, expected_object_type="survey") # Validates the type
441
564
  """
442
565
  obj_uuid, owner_username, alias = self._resolve_uuid_or_alias(url_or_uuid)
443
566
 
@@ -459,6 +582,7 @@ class Coop(CoopFunctionsMixin):
459
582
  if "load_from:" in json_string[0:12]:
460
583
  load_link = json_string.split("load_from:")[1]
461
584
  object_data = requests.get(load_link)
585
+ self._resolve_gcs_response(object_data)
462
586
  json_string = object_data.text
463
587
  object_type = response.json().get("object_type")
464
588
  if expected_object_type and object_type != expected_object_type:
@@ -485,6 +609,7 @@ class Coop(CoopFunctionsMixin):
485
609
  if "load_from:" in json_string[0:12]:
486
610
  load_link = json_string.split("load_from:")[1]
487
611
  object_data = requests.get(load_link)
612
+ self._resolve_gcs_response(object_data)
488
613
  json_string = object_data.text
489
614
 
490
615
  json_string = json.loads(json_string)
@@ -848,7 +973,7 @@ class Coop(CoopFunctionsMixin):
848
973
 
849
974
  def remote_inference_create(
850
975
  self,
851
- job: Jobs,
976
+ job: "Jobs",
852
977
  description: Optional[str] = None,
853
978
  status: RemoteJobStatus = "queued",
854
979
  visibility: Optional[VisibilityType] = "unlisted",
@@ -857,17 +982,48 @@ class Coop(CoopFunctionsMixin):
857
982
  fresh: Optional[bool] = False,
858
983
  ) -> RemoteInferenceCreationInfo:
859
984
  """
860
- Send a remote inference job to the server.
861
-
862
- :param job: The EDSL job to send to the server.
863
- :param optional description: A description for this entry in the remote cache.
864
- :param status: The status of the job. Should be 'queued', unless you are debugging.
865
- :param visibility: The visibility of the cache entry.
866
- :param iterations: The number of times to run each interview.
985
+ Create a remote inference job for execution in the Expected Parrot cloud.
986
+
987
+ This method sends a job to be executed in the cloud, which can be more efficient
988
+ for large jobs or when you want to run jobs in the background. The job execution
989
+ is handled by Expected Parrot's infrastructure, and you can check the status
990
+ and retrieve results later.
991
+
992
+ Parameters:
993
+ job (Jobs): The EDSL job to run in the cloud
994
+ description (str, optional): A human-readable description of the job
995
+ status (RemoteJobStatus): Initial status, should be "queued" for normal use
996
+ Possible values: "queued", "running", "completed", "failed"
997
+ visibility (VisibilityType): Access level for the job information. One of:
998
+ - "private": Only accessible by the owner
999
+ - "public": Accessible by anyone
1000
+ - "unlisted": Accessible with the link, but not listed publicly
1001
+ initial_results_visibility (VisibilityType): Access level for the job results
1002
+ iterations (int): Number of times to run each interview (default: 1)
1003
+ fresh (bool): If True, ignore existing cache entries and generate new results
867
1004
 
868
- >>> job = Jobs.example()
869
- >>> coop.remote_inference_create(job=job, description="My job")
870
- {'uuid': '9f8484ee-b407-40e4-9652-4133a7236c9c', 'description': 'My job', 'status': 'queued', 'iterations': None, 'visibility': 'unlisted', 'version': '0.1.38.dev1'}
1005
+ Returns:
1006
+ RemoteInferenceCreationInfo: Information about the created job including:
1007
+ - uuid: The unique identifier for the job
1008
+ - description: The job description
1009
+ - status: Current status of the job
1010
+ - iterations: Number of iterations for each interview
1011
+ - visibility: Access level for the job
1012
+ - version: EDSL version used to create the job
1013
+
1014
+ Raises:
1015
+ CoopServerResponseError: If there's an error communicating with the server
1016
+
1017
+ Notes:
1018
+ - Remote jobs run asynchronously and may take time to complete
1019
+ - Use remote_inference_get() with the returned UUID to check status
1020
+ - Credits are consumed based on the complexity of the job
1021
+
1022
+ Example:
1023
+ >>> from edsl.jobs import Jobs
1024
+ >>> job = Jobs.example()
1025
+ >>> job_info = coop.remote_inference_create(job=job, description="My job")
1026
+ >>> print(f"Job created with UUID: {job_info['uuid']}")
871
1027
  """
872
1028
  response = self._send_server_request(
873
1029
  uri="api/v0/remote-inference",
@@ -904,15 +1060,43 @@ class Coop(CoopFunctionsMixin):
904
1060
  self, job_uuid: Optional[str] = None, results_uuid: Optional[str] = None
905
1061
  ) -> RemoteInferenceResponse:
906
1062
  """
907
- Get the details of a remote inference job.
908
- You can pass either the job uuid or the results uuid as a parameter.
909
- If you pass both, the job uuid will be prioritized.
1063
+ Get the status and details of a remote inference job.
1064
+
1065
+ This method retrieves the current status and information about a remote job,
1066
+ including links to results if the job has completed successfully.
910
1067
 
911
- :param job_uuid: The UUID of the EDSL job.
912
- :param results_uuid: The UUID of the results associated with the EDSL job.
1068
+ Parameters:
1069
+ job_uuid (str, optional): The UUID of the remote job to check
1070
+ results_uuid (str, optional): The UUID of the results associated with the job
1071
+ (can be used if you only have the results UUID)
913
1072
 
914
- >>> coop.remote_inference_get("9f8484ee-b407-40e4-9652-4133a7236c9c")
915
- {'job_uuid': '9f8484ee-b407-40e4-9652-4133a7236c9c', 'results_uuid': 'dd708234-31bf-4fe1-8747-6e232625e026', 'results_url': 'https://www.expectedparrot.com/content/dd708234-31bf-4fe1-8747-6e232625e026', 'latest_error_report_uuid': None, 'latest_error_report_url': None, 'status': 'completed', 'reason': None, 'credits_consumed': 0.35, 'version': '0.1.38.dev1'}
1073
+ Returns:
1074
+ RemoteInferenceResponse: Information about the job including:
1075
+ - job_uuid: The unique identifier for the job
1076
+ - results_uuid: The UUID of the results (if job is completed)
1077
+ - results_url: URL to access the results (if available)
1078
+ - latest_error_report_uuid: UUID of error report (if job failed)
1079
+ - latest_error_report_url: URL to access error details (if available)
1080
+ - status: Current status ("queued", "running", "completed", "failed")
1081
+ - reason: Reason for failure (if applicable)
1082
+ - credits_consumed: Credits used for the job execution
1083
+ - version: EDSL version used for the job
1084
+
1085
+ Raises:
1086
+ ValueError: If neither job_uuid nor results_uuid is provided
1087
+ CoopServerResponseError: If there's an error communicating with the server
1088
+
1089
+ Notes:
1090
+ - Either job_uuid or results_uuid must be provided
1091
+ - If both are provided, job_uuid takes precedence
1092
+ - For completed jobs, you can use the results_url to view or download results
1093
+ - For failed jobs, check the latest_error_report_url for debugging information
1094
+
1095
+ Example:
1096
+ >>> job_status = coop.remote_inference_get("9f8484ee-b407-40e4-9652-4133a7236c9c")
1097
+ >>> print(f"Job status: {job_status['status']}")
1098
+ >>> if job_status['status'] == 'completed':
1099
+ ... print(f"Results available at: {job_status['results_url']}")
916
1100
  """
917
1101
  if job_uuid is None and results_uuid is None:
918
1102
  raise ValueError("Either job_uuid or results_uuid must be provided.")
@@ -970,7 +1154,7 @@ class Coop(CoopFunctionsMixin):
970
1154
  return response.json().get("running_jobs", [])
971
1155
 
972
1156
  def remote_inference_cost(
973
- self, input: Union[Jobs, Survey], iterations: int = 1
1157
+ self, input: Union["Jobs", "Survey"], iterations: int = 1
974
1158
  ) -> int:
975
1159
  """
976
1160
  Get the cost of a remote inference job.
@@ -981,6 +1165,9 @@ class Coop(CoopFunctionsMixin):
981
1165
  >>> coop.remote_inference_cost(input=job)
982
1166
  {'credits': 0.77, 'usd': 0.0076950000000000005}
983
1167
  """
1168
+ from ..jobs import Jobs
1169
+ from ..surveys import Survey
1170
+
984
1171
  if isinstance(input, Jobs):
985
1172
  job = input
986
1173
  elif isinstance(input, Survey):
@@ -1011,7 +1198,7 @@ class Coop(CoopFunctionsMixin):
1011
1198
  ################
1012
1199
  def create_project(
1013
1200
  self,
1014
- survey: Survey,
1201
+ survey: "Survey",
1015
1202
  project_name: str = "Project",
1016
1203
  survey_description: Optional[str] = None,
1017
1204
  survey_alias: Optional[str] = None,
@@ -1041,16 +1228,10 @@ class Coop(CoopFunctionsMixin):
1041
1228
  "respondent_url": f"{self.url}/respond/{response_json.get('uuid')}",
1042
1229
  }
1043
1230
 
1044
- ################
1045
- # DUNDER METHODS
1046
- ################
1047
1231
  def __repr__(self):
1048
1232
  """Return a string representation of the client."""
1049
1233
  return f"Client(api_key='{self.api_key}', url='{self.url}')"
1050
1234
 
1051
- ################
1052
- # EXPERIMENTAL
1053
- ################
1054
1235
  async def remote_async_execute_model_call(
1055
1236
  self, model_dict: dict, user_prompt: str, system_prompt: str
1056
1237
  ) -> dict:
@@ -1087,12 +1268,39 @@ class Coop(CoopFunctionsMixin):
1087
1268
 
1088
1269
  def fetch_prices(self) -> dict:
1089
1270
  """
1090
- Fetch model prices from Coop. If the request fails, return an empty dict.
1091
- """
1271
+ Fetch the current pricing information for language models.
1092
1272
 
1093
- from edsl.coop.PriceFetcher import PriceFetcher
1273
+ This method retrieves the latest pricing information for all supported language models
1274
+ from the Expected Parrot API. The pricing data is used to estimate costs for jobs
1275
+ and to optimize model selection based on budget constraints.
1094
1276
 
1095
- from edsl.config import CONFIG
1277
+ Returns:
1278
+ dict: A dictionary mapping (service, model) tuples to pricing information.
1279
+ Each entry contains token pricing for input and output tokens.
1280
+ Example structure:
1281
+ {
1282
+ ('openai', 'gpt-4'): {
1283
+ 'input': {'usd_per_1M_tokens': 30.0, ...},
1284
+ 'output': {'usd_per_1M_tokens': 60.0, ...}
1285
+ }
1286
+ }
1287
+
1288
+ Raises:
1289
+ ValueError: If the EDSL_FETCH_TOKEN_PRICES configuration setting is invalid
1290
+
1291
+ Notes:
1292
+ - Returns an empty dict if EDSL_FETCH_TOKEN_PRICES is set to "False"
1293
+ - The pricing data is cached to minimize API calls
1294
+ - Pricing may vary based on the model, provider, and token type (input/output)
1295
+ - All prices are in USD per million tokens
1296
+
1297
+ Example:
1298
+ >>> prices = coop.fetch_prices()
1299
+ >>> gpt4_price = prices.get(('openai', 'gpt-4'), {})
1300
+ >>> print(f"GPT-4 input price: ${gpt4_price.get('input', {}).get('usd_per_1M_tokens')}")
1301
+ """
1302
+ from .price_fetcher import PriceFetcher
1303
+ from ..config import CONFIG
1096
1304
 
1097
1305
  if CONFIG.get("EDSL_FETCH_TOKEN_PRICES") == "True":
1098
1306
  price_fetcher = PriceFetcher()
@@ -1106,9 +1314,37 @@ class Coop(CoopFunctionsMixin):
1106
1314
 
1107
1315
  def fetch_models(self) -> ServiceToModelsMapping:
1108
1316
  """
1109
- Fetch a dict of available models from Coop.
1317
+ Fetch information about available language models from Expected Parrot.
1318
+
1319
+ This method retrieves the current list of available language models grouped
1320
+ by service provider (e.g., OpenAI, Anthropic, etc.). This information is
1321
+ useful for programmatically selecting models based on availability and
1322
+ for ensuring that jobs only use supported models.
1110
1323
 
1111
- Each key in the dict is an inference service, and each value is a list of models from that service.
1324
+ Returns:
1325
+ ServiceToModelsMapping: A mapping of service providers to their available models.
1326
+ Example structure:
1327
+ {
1328
+ "openai": ["gpt-4", "gpt-3.5-turbo", ...],
1329
+ "anthropic": ["claude-3-opus", "claude-3-sonnet", ...],
1330
+ ...
1331
+ }
1332
+
1333
+ Raises:
1334
+ CoopServerResponseError: If there's an error communicating with the server
1335
+
1336
+ Notes:
1337
+ - The availability of models may change over time
1338
+ - Not all models may be accessible with your current API keys
1339
+ - Use this method to check for model availability before creating jobs
1340
+ - Models may have different capabilities (text-only, multimodal, etc.)
1341
+
1342
+ Example:
1343
+ >>> models = coop.fetch_models()
1344
+ >>> if "gpt-4" in models.get("openai", []):
1345
+ ... print("GPT-4 is available")
1346
+ >>> available_services = list(models.keys())
1347
+ >>> print(f"Available services: {available_services}")
1112
1348
  """
1113
1349
  response = self._send_server_request(uri="api/v0/models", method="GET")
1114
1350
  self._resolve_server_response(response)
@@ -1217,7 +1453,7 @@ class Coop(CoopFunctionsMixin):
1217
1453
  """
1218
1454
  import secrets
1219
1455
  from dotenv import load_dotenv
1220
- from edsl.utilities.utilities import write_api_key_to_env
1456
+ from ..utilities.utilities import write_api_key_to_env
1221
1457
 
1222
1458
  edsl_auth_token = secrets.token_urlsafe(16)
1223
1459
 
@@ -1255,9 +1491,9 @@ def main():
1255
1491
  ScenarioList,
1256
1492
  Survey,
1257
1493
  )
1258
- from edsl.coop import Coop
1259
- from edsl.data.CacheEntry import CacheEntry
1260
- from edsl.jobs import Jobs
1494
+ from ..coop import Coop
1495
+ from ..caching import CacheEntry
1496
+ from ..jobs import Jobs
1261
1497
 
1262
1498
  # init & basics
1263
1499
  API_KEY = "b"