edsl 0.1.47__py3-none-any.whl → 0.1.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/__init__.py +44 -39
- edsl/__version__.py +1 -1
- edsl/agents/__init__.py +4 -2
- edsl/agents/{Agent.py → agent.py} +442 -152
- edsl/agents/{AgentList.py → agent_list.py} +220 -162
- edsl/agents/descriptors.py +46 -7
- edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
- edsl/base/__init__.py +75 -0
- edsl/base/base_class.py +1303 -0
- edsl/base/data_transfer_models.py +114 -0
- edsl/base/enums.py +215 -0
- edsl/base.py +8 -0
- edsl/buckets/__init__.py +25 -0
- edsl/buckets/bucket_collection.py +324 -0
- edsl/buckets/model_buckets.py +206 -0
- edsl/buckets/token_bucket.py +502 -0
- edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
- edsl/buckets/token_bucket_client.py +509 -0
- edsl/caching/__init__.py +20 -0
- edsl/caching/cache.py +814 -0
- edsl/caching/cache_entry.py +427 -0
- edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
- edsl/caching/exceptions.py +24 -0
- edsl/caching/orm.py +30 -0
- edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
- edsl/caching/sql_dict.py +441 -0
- edsl/config/__init__.py +8 -0
- edsl/config/config_class.py +177 -0
- edsl/config.py +4 -176
- edsl/conversation/Conversation.py +7 -7
- edsl/conversation/car_buying.py +4 -4
- edsl/conversation/chips.py +6 -6
- edsl/coop/__init__.py +25 -2
- edsl/coop/coop.py +303 -67
- edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
- edsl/coop/exceptions.py +62 -0
- edsl/coop/price_fetcher.py +126 -0
- edsl/coop/utils.py +89 -24
- edsl/data_transfer_models.py +5 -72
- edsl/dataset/__init__.py +10 -0
- edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
- edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
- edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
- edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
- edsl/{results → dataset/display}/table_renderers.py +58 -2
- edsl/{results → dataset}/file_exports.py +4 -5
- edsl/{results → dataset}/smart_objects.py +2 -2
- edsl/enums.py +5 -205
- edsl/inference_services/__init__.py +5 -0
- edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
- edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
- edsl/inference_services/data_structures.py +3 -2
- edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
- edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
- edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
- edsl/inference_services/registry.py +4 -41
- edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
- edsl/inference_services/services/__init__.py +31 -0
- edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
- edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
- edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
- edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
- edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
- edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
- edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
- edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
- edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
- edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
- edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
- edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
- edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
- edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
- edsl/inference_services/write_available.py +1 -2
- edsl/instructions/__init__.py +6 -0
- edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
- edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
- edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
- edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
- edsl/interviews/__init__.py +4 -0
- edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
- edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
- edsl/interviews/interview.py +638 -0
- edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
- edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
- edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
- edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
- edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
- edsl/invigilators/__init__.py +38 -0
- edsl/invigilators/invigilator_base.py +477 -0
- edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
- edsl/invigilators/prompt_constructor.py +476 -0
- edsl/{agents → invigilators}/prompt_helpers.py +2 -1
- edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
- edsl/{agents → invigilators}/question_option_processor.py +96 -21
- edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
- edsl/jobs/__init__.py +7 -1
- edsl/jobs/async_interview_runner.py +99 -35
- edsl/jobs/check_survey_scenario_compatibility.py +7 -5
- edsl/jobs/data_structures.py +153 -22
- edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
- edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
- edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
- edsl/jobs/{Jobs.py → jobs.py} +313 -167
- edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
- edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
- edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
- edsl/jobs/jobs_pricing_estimation.py +347 -0
- edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
- edsl/jobs/jobs_runner_asyncio.py +282 -0
- edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/key_management/__init__.py +28 -0
- edsl/key_management/key_lookup.py +161 -0
- edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
- edsl/key_management/key_lookup_collection.py +82 -0
- edsl/key_management/models.py +218 -0
- edsl/language_models/__init__.py +7 -2
- edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
- edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
- edsl/language_models/language_model.py +1080 -0
- edsl/language_models/model.py +10 -25
- edsl/language_models/{ModelList.py → model_list.py} +9 -14
- edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
- edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
- edsl/language_models/repair.py +4 -4
- edsl/language_models/utilities.py +4 -4
- edsl/notebooks/__init__.py +3 -1
- edsl/notebooks/{Notebook.py → notebook.py} +7 -8
- edsl/prompts/__init__.py +1 -1
- edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
- edsl/prompts/{Prompt.py → prompt.py} +101 -95
- edsl/questions/HTMLQuestion.py +1 -1
- edsl/questions/__init__.py +154 -25
- edsl/questions/answer_validator_mixin.py +1 -1
- edsl/questions/compose_questions.py +4 -3
- edsl/questions/derived/question_likert_five.py +166 -0
- edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
- edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
- edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
- edsl/questions/descriptors.py +24 -30
- edsl/questions/loop_processor.py +65 -19
- edsl/questions/question_base.py +881 -0
- edsl/questions/question_base_gen_mixin.py +15 -16
- edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
- edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
- edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
- edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
- edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
- edsl/questions/question_free_text.py +282 -0
- edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
- edsl/questions/{QuestionList.py → question_list.py} +6 -7
- edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
- edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
- edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
- edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
- edsl/questions/question_registry.py +4 -9
- edsl/questions/register_questions_meta.py +8 -4
- edsl/questions/response_validator_abc.py +17 -16
- edsl/results/__init__.py +4 -1
- edsl/{exceptions/results.py → results/exceptions.py} +1 -1
- edsl/results/report.py +197 -0
- edsl/results/{Result.py → result.py} +131 -45
- edsl/results/{Results.py → results.py} +365 -220
- edsl/results/results_selector.py +344 -25
- edsl/scenarios/__init__.py +30 -3
- edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
- edsl/scenarios/directory_scanner.py +156 -13
- edsl/scenarios/document_chunker.py +186 -0
- edsl/scenarios/exceptions.py +101 -0
- edsl/scenarios/file_methods.py +2 -3
- edsl/scenarios/{FileStore.py → file_store.py} +275 -189
- edsl/scenarios/handlers/__init__.py +14 -14
- edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
- edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
- edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
- edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
- edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
- edsl/scenarios/handlers/latex_file_store.py +5 -0
- edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
- edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
- edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
- edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
- edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
- edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
- edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
- edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
- edsl/scenarios/scenario.py +928 -0
- edsl/scenarios/scenario_join.py +18 -5
- edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
- edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
- edsl/scenarios/scenario_selector.py +5 -1
- edsl/study/ObjectEntry.py +2 -2
- edsl/study/SnapShot.py +5 -5
- edsl/study/Study.py +18 -19
- edsl/study/__init__.py +6 -4
- edsl/surveys/__init__.py +7 -4
- edsl/surveys/dag/__init__.py +2 -0
- edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
- edsl/surveys/{DAG.py → dag/dag.py} +13 -10
- edsl/surveys/descriptors.py +1 -1
- edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
- edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
- edsl/surveys/memory/__init__.py +3 -0
- edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
- edsl/surveys/rules/__init__.py +3 -0
- edsl/surveys/{Rule.py → rules/rule.py} +103 -43
- edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
- edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
- edsl/surveys/survey.py +1743 -0
- edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
- edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
- edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
- edsl/tasks/__init__.py +32 -0
- edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
- edsl/tasks/task_creators.py +135 -0
- edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
- edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
- edsl/tasks/task_status_log.py +85 -0
- edsl/tokens/__init__.py +2 -0
- edsl/tokens/interview_token_usage.py +53 -0
- edsl/utilities/PrettyList.py +1 -1
- edsl/utilities/SystemInfo.py +25 -22
- edsl/utilities/__init__.py +29 -21
- edsl/utilities/gcp_bucket/__init__.py +2 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
- edsl/utilities/interface.py +44 -536
- edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
- edsl/utilities/repair_functions.py +1 -1
- {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/METADATA +1 -1
- edsl-0.1.48.dist-info/RECORD +347 -0
- edsl/Base.py +0 -493
- edsl/BaseDiff.py +0 -260
- edsl/agents/InvigilatorBase.py +0 -260
- edsl/agents/PromptConstructor.py +0 -318
- edsl/coop/PriceFetcher.py +0 -54
- edsl/data/Cache.py +0 -582
- edsl/data/CacheEntry.py +0 -238
- edsl/data/SQLiteDict.py +0 -292
- edsl/data/__init__.py +0 -5
- edsl/data/orm.py +0 -10
- edsl/exceptions/cache.py +0 -5
- edsl/exceptions/coop.py +0 -14
- edsl/exceptions/data.py +0 -14
- edsl/exceptions/scenarios.py +0 -29
- edsl/jobs/Answers.py +0 -43
- edsl/jobs/JobsPrompts.py +0 -354
- edsl/jobs/buckets/BucketCollection.py +0 -134
- edsl/jobs/buckets/ModelBuckets.py +0 -65
- edsl/jobs/buckets/TokenBucket.py +0 -283
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/interviews/Interview.py +0 -395
- edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
- edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
- edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
- edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
- edsl/jobs/tasks/TaskCreators.py +0 -64
- edsl/jobs/tasks/TaskStatusLog.py +0 -23
- edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
- edsl/language_models/LanguageModel.py +0 -635
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/models.py +0 -137
- edsl/questions/QuestionBase.py +0 -544
- edsl/questions/QuestionFreeText.py +0 -130
- edsl/questions/derived/QuestionLikertFive.py +0 -76
- edsl/results/ResultsExportMixin.py +0 -45
- edsl/results/TextEditor.py +0 -50
- edsl/results/results_fetch_mixin.py +0 -33
- edsl/results/results_tools_mixin.py +0 -98
- edsl/scenarios/DocumentChunker.py +0 -104
- edsl/scenarios/Scenario.py +0 -548
- edsl/scenarios/ScenarioHtmlMixin.py +0 -65
- edsl/scenarios/ScenarioListExportMixin.py +0 -45
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/shared.py +0 -1
- edsl/surveys/Survey.py +0 -1301
- edsl/surveys/SurveyQualtricsImport.py +0 -284
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/tools/__init__.py +0 -1
- edsl/tools/clusters.py +0 -192
- edsl/tools/embeddings.py +0 -27
- edsl/tools/embeddings_plotting.py +0 -118
- edsl/tools/plotting.py +0 -112
- edsl/tools/summarize.py +0 -18
- edsl/utilities/data/Registry.py +0 -6
- edsl/utilities/data/__init__.py +0 -1
- edsl/utilities/data/scooter_results.json +0 -1
- edsl-0.1.47.dist-info/RECORD +0 -354
- /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
- /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
- /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
- /edsl/{results → dataset/display}/table_data_class.py +0 -0
- /edsl/{results → dataset/display}/table_display.css +0 -0
- /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
- /edsl/{results → dataset}/tree_explore.py +0 -0
- /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
- /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
- /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
- /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
- /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
- /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
- /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
- /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
- /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
- /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
- /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
- /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
- /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
- /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
- /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
- {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
- {edsl-0.1.47.dist-info → edsl-0.1.48.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
|
8
|
+
from .. import __version__
|
9
9
|
|
10
|
-
from
|
11
|
-
from
|
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
|
-
|
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
|
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
|
29
|
-
from
|
30
|
+
from .coop_functions import CoopFunctionsMixin
|
31
|
+
from .ep_key_handling import ExpectedParrotKeyHandler
|
30
32
|
|
31
|
-
from
|
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
|
-
|
66
|
-
|
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 =
|
126
|
+
self._edsl_version = __version__
|
83
127
|
|
84
128
|
def get_progress_bar_url(self):
|
85
129
|
return f"{CONFIG.EXPECTED_PARROT_URL}"
|
@@ -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
|
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
|
-
|
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":
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
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
|
-
|
437
|
-
|
438
|
-
|
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
|
-
:
|
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
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
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
|
-
|
869
|
-
|
870
|
-
|
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
|
-
|
909
|
-
|
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
|
-
:
|
912
|
-
|
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
|
-
|
915
|
-
|
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
|
1091
|
-
"""
|
1271
|
+
Fetch the current pricing information for language models.
|
1092
1272
|
|
1093
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
1259
|
-
from
|
1260
|
-
from
|
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"
|