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
@@ -17,16 +17,55 @@ def get_input_with_timeout(prompt, timeout=5, default="y"):
|
|
17
17
|
|
18
18
|
|
19
19
|
class ExpectedParrotKeyHandler:
|
20
|
+
"""
|
21
|
+
Manages Expected Parrot API keys for user authentication.
|
22
|
+
|
23
|
+
This class handles the storage, retrieval, and management of Expected Parrot API keys.
|
24
|
+
It provides functionality to securely store API keys in platform-specific user
|
25
|
+
configuration directories and retrieve them when needed. It also handles key
|
26
|
+
preference management (e.g., environment variables vs. stored keys).
|
27
|
+
|
28
|
+
The key handler follows a priority order when retrieving keys:
|
29
|
+
1. Environment variables (EXPECTED_PARROT_API_KEY)
|
30
|
+
2. Platform-specific user config directory
|
31
|
+
|
32
|
+
Attributes:
|
33
|
+
asked_to_store_file_name (str): Filename for tracking if user was asked about storage
|
34
|
+
ep_key_file_name (str): Filename for the stored API key
|
35
|
+
application_name (str): Application name for the config directory
|
36
|
+
"""
|
20
37
|
asked_to_store_file_name = "asked_to_store.txt"
|
21
38
|
ep_key_file_name = "ep_api_key.txt"
|
22
39
|
application_name = "edsl"
|
23
40
|
|
24
41
|
@property
|
25
|
-
def config_dir(self):
|
42
|
+
def config_dir(self) -> str:
|
43
|
+
"""
|
44
|
+
Get the platform-specific user configuration directory for the application.
|
45
|
+
|
46
|
+
This property uses the platformdirs library to determine the appropriate
|
47
|
+
location for storing configuration files based on the user's operating system.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
str: Path to the user configuration directory
|
51
|
+
|
52
|
+
Notes:
|
53
|
+
- On Windows, typically: C:\\Users\\<username>\\AppData\\Local\\edsl
|
54
|
+
- On macOS, typically: ~/Library/Application Support/edsl
|
55
|
+
- On Linux, typically: ~/.config/edsl
|
56
|
+
"""
|
26
57
|
return platformdirs.user_config_dir(self.application_name)
|
27
58
|
|
28
59
|
def _ep_key_file_exists(self) -> bool:
|
29
|
-
"""
|
60
|
+
"""
|
61
|
+
Check if the Expected Parrot key file exists in the config directory.
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
bool: True if the key file exists, False otherwise
|
65
|
+
|
66
|
+
Notes:
|
67
|
+
- Does not check the validity of the stored key
|
68
|
+
"""
|
30
69
|
return Path(self.config_dir).joinpath(self.ep_key_file_name).exists()
|
31
70
|
|
32
71
|
def ok_to_ask_to_store(self):
|
@@ -73,37 +112,59 @@ class ExpectedParrotKeyHandler:
|
|
73
112
|
f.write("Yes")
|
74
113
|
return False
|
75
114
|
|
76
|
-
def get_ep_api_key(self):
|
77
|
-
|
115
|
+
def get_ep_api_key(self) -> str:
|
116
|
+
"""
|
117
|
+
Retrieve the Expected Parrot API key from available sources.
|
118
|
+
|
119
|
+
This method checks multiple sources for the API key, with the following priority:
|
120
|
+
1. Environment variable (EXPECTED_PARROT_API_KEY)
|
121
|
+
2. Stored key in the user's config directory
|
122
|
+
|
123
|
+
If keys are found in multiple sources and they differ, the environment
|
124
|
+
variable takes precedence. A warning is issued in this case.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
str: The Expected Parrot API key, or None if not found
|
128
|
+
|
129
|
+
Notes:
|
130
|
+
- If a key is found, it will attempt to store it persistently if appropriate
|
131
|
+
- Warnings are issued if conflicting keys are found in different sources
|
132
|
+
- Environment variables always take precedence over stored keys
|
133
|
+
"""
|
134
|
+
# Initialize variables
|
78
135
|
api_key = None
|
79
136
|
api_key_from_cache = None
|
80
137
|
api_key_from_os = None
|
81
138
|
|
139
|
+
# Try to get key from config directory
|
82
140
|
if self._ep_key_file_exists():
|
83
141
|
with open(Path(self.config_dir).joinpath(self.ep_key_file_name), "r") as f:
|
84
142
|
api_key_from_cache = f.read().strip()
|
85
143
|
|
144
|
+
# Try to get key from environment variable
|
86
145
|
api_key_from_os = os.getenv("EXPECTED_PARROT_API_KEY")
|
87
146
|
|
147
|
+
# Handle the case where both sources have keys
|
88
148
|
if api_key_from_os and api_key_from_cache:
|
89
149
|
if api_key_from_os != api_key_from_cache:
|
90
150
|
import warnings
|
91
|
-
|
92
151
|
warnings.warn(
|
93
152
|
"WARNING: The Expected Parrot API key from the environment variable "
|
94
153
|
"differs from the one stored in the config directory. Using the one "
|
95
154
|
"from the environment variable."
|
96
155
|
)
|
97
156
|
api_key = api_key_from_os
|
98
|
-
|
99
|
-
|
157
|
+
# Handle the case where only OS environment has key
|
158
|
+
elif api_key_from_os:
|
100
159
|
api_key = api_key_from_os
|
101
|
-
|
102
|
-
|
160
|
+
# Handle the case where only cached key exists
|
161
|
+
elif api_key_from_cache:
|
103
162
|
api_key = api_key_from_cache
|
104
163
|
|
164
|
+
# If a key was found, ask to store it persistently
|
105
165
|
if api_key is not None:
|
106
166
|
_ = self.ask_to_store(api_key)
|
167
|
+
|
107
168
|
return api_key
|
108
169
|
|
109
170
|
def delete_ep_api_key(self):
|
@@ -112,7 +173,22 @@ class ExpectedParrotKeyHandler:
|
|
112
173
|
os.remove(key_path)
|
113
174
|
print("Deleted Expected Parrot API key at ", key_path)
|
114
175
|
|
115
|
-
def store_ep_api_key(self, api_key):
|
176
|
+
def store_ep_api_key(self, api_key: str) -> None:
|
177
|
+
"""
|
178
|
+
Store the Expected Parrot API key in the user's config directory.
|
179
|
+
|
180
|
+
This method saves the provided API key to a file in the platform-specific
|
181
|
+
user configuration directory, creating the directory if it doesn't exist.
|
182
|
+
|
183
|
+
Parameters:
|
184
|
+
api_key (str): The API key to store
|
185
|
+
|
186
|
+
Notes:
|
187
|
+
- The key is stored in plain text in the user's config directory
|
188
|
+
- The directory is created if it doesn't exist
|
189
|
+
- Any existing key file will be overwritten
|
190
|
+
- The location of the config directory is platform-specific
|
191
|
+
"""
|
116
192
|
# Create the directory if it doesn't exist
|
117
193
|
os.makedirs(self.config_dir, exist_ok=True)
|
118
194
|
|
edsl/coop/exceptions.py
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
"""
|
2
|
+
Exceptions specific to the Expected Parrot cloud service integration.
|
3
|
+
|
4
|
+
This module defines exception classes for various error conditions that can
|
5
|
+
occur when interacting with Expected Parrot's cloud services through the
|
6
|
+
Coop module. These exceptions help with specific error handling and reporting.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from ..base import BaseException
|
10
|
+
|
11
|
+
|
12
|
+
class CoopErrors(BaseException):
|
13
|
+
"""
|
14
|
+
Base class for all Coop-related exceptions.
|
15
|
+
|
16
|
+
This is the parent class for all exceptions raised by the Coop module.
|
17
|
+
It inherits from EDSL's BaseException to maintain consistency with
|
18
|
+
the library's exception hierarchy.
|
19
|
+
"""
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
class CoopInvalidURLError(CoopErrors):
|
24
|
+
"""
|
25
|
+
Exception raised when an invalid URL format is provided.
|
26
|
+
|
27
|
+
This exception is raised when a URL provided to the Coop client
|
28
|
+
does not match the expected format for object or resource URLs.
|
29
|
+
|
30
|
+
Example:
|
31
|
+
When a URL doesn't follow the pattern:
|
32
|
+
- https://expectedparrot.com/content/{uuid}
|
33
|
+
- https://expectedparrot.com/content/{username}/{alias}
|
34
|
+
"""
|
35
|
+
pass
|
36
|
+
|
37
|
+
|
38
|
+
class CoopNoUUIDError(CoopErrors):
|
39
|
+
"""
|
40
|
+
Exception raised when a required UUID or identifier is missing.
|
41
|
+
|
42
|
+
This exception is raised when an operation requires a UUID or other
|
43
|
+
identifier, but none was provided.
|
44
|
+
|
45
|
+
Example:
|
46
|
+
When calling get() without providing a UUID or URL
|
47
|
+
"""
|
48
|
+
pass
|
49
|
+
|
50
|
+
|
51
|
+
class CoopServerResponseError(CoopErrors):
|
52
|
+
"""
|
53
|
+
Exception raised when the server returns an error response.
|
54
|
+
|
55
|
+
This exception is raised when the Expected Parrot API returns an error
|
56
|
+
response, such as authentication failures, rate limits, or server errors.
|
57
|
+
The exception message typically includes the error details from the server.
|
58
|
+
|
59
|
+
Example:
|
60
|
+
When the server returns a 401 Unauthorized response due to an invalid API key
|
61
|
+
"""
|
62
|
+
pass
|
@@ -0,0 +1,126 @@
|
|
1
|
+
"""
|
2
|
+
Module for retrieving and caching language model pricing information.
|
3
|
+
|
4
|
+
This module provides functionality to fetch current pricing information for various
|
5
|
+
language models from the Expected Parrot API. It uses a singleton pattern to ensure
|
6
|
+
that price information is only fetched once and then cached for efficiency.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import requests
|
10
|
+
import csv
|
11
|
+
from io import StringIO
|
12
|
+
from typing import Dict, Tuple, Optional, Any
|
13
|
+
|
14
|
+
|
15
|
+
class PriceFetcher:
|
16
|
+
"""
|
17
|
+
A singleton class for fetching and caching language model pricing information.
|
18
|
+
|
19
|
+
This class retrieves the current pricing for language models from the Expected
|
20
|
+
Parrot API and caches it to avoid unnecessary network requests. It implements
|
21
|
+
a singleton pattern to ensure that only one instance exists throughout the
|
22
|
+
application.
|
23
|
+
|
24
|
+
Attributes:
|
25
|
+
_instance (PriceFetcher): The singleton instance of the class
|
26
|
+
_cached_prices (Dict[Tuple[str, str], Dict]): Cached pricing information
|
27
|
+
mapping (service, model) tuples to their pricing details
|
28
|
+
"""
|
29
|
+
_instance = None
|
30
|
+
|
31
|
+
def __new__(cls):
|
32
|
+
"""
|
33
|
+
Create or return the singleton instance of PriceFetcher.
|
34
|
+
|
35
|
+
This method ensures that only one instance of PriceFetcher exists.
|
36
|
+
When called multiple times, it returns the same instance.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
PriceFetcher: The singleton instance
|
40
|
+
"""
|
41
|
+
if cls._instance is None:
|
42
|
+
cls._instance = super(PriceFetcher, cls).__new__(cls)
|
43
|
+
cls._instance._cached_prices = None
|
44
|
+
return cls._instance
|
45
|
+
|
46
|
+
def fetch_prices(self) -> Dict[Tuple[str, str], Dict[str, Dict[str, Any]]]:
|
47
|
+
"""
|
48
|
+
Fetch current pricing information for language models.
|
49
|
+
|
50
|
+
This method retrieves the latest pricing information from the Expected Parrot API
|
51
|
+
for all supported language models. It caches the results to avoid redundant API calls.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Dict[Tuple[str, str], Dict[str, Dict[str, Any]]]: A dictionary mapping
|
55
|
+
(service, model) tuples to their pricing information for different token types.
|
56
|
+
Structure example:
|
57
|
+
{
|
58
|
+
('openai', 'gpt-4'): {
|
59
|
+
'input': {
|
60
|
+
'service': 'openai',
|
61
|
+
'model': 'gpt-4',
|
62
|
+
'token_type': 'input',
|
63
|
+
'usd_per_1M_tokens': 30.0,
|
64
|
+
...
|
65
|
+
},
|
66
|
+
'output': {
|
67
|
+
'service': 'openai',
|
68
|
+
'model': 'gpt-4',
|
69
|
+
'token_type': 'output',
|
70
|
+
'usd_per_1M_tokens': 60.0,
|
71
|
+
...
|
72
|
+
}
|
73
|
+
},
|
74
|
+
...
|
75
|
+
}
|
76
|
+
|
77
|
+
Notes:
|
78
|
+
- If the request fails, returns an empty dictionary
|
79
|
+
- Uses caching to avoid redundant API calls
|
80
|
+
- Pricing data includes both input and output token costs
|
81
|
+
- This method is automatically called by the Coop.fetch_prices() method
|
82
|
+
"""
|
83
|
+
# Return cached prices if available
|
84
|
+
if self._cached_prices is not None:
|
85
|
+
return self._cached_prices
|
86
|
+
|
87
|
+
import os
|
88
|
+
import requests
|
89
|
+
from ..config import CONFIG
|
90
|
+
|
91
|
+
try:
|
92
|
+
# Fetch the pricing data
|
93
|
+
url = f"{CONFIG.EXPECTED_PARROT_URL}/api/v0/prices"
|
94
|
+
api_key = os.getenv("EXPECTED_PARROT_API_KEY")
|
95
|
+
headers = {}
|
96
|
+
if api_key:
|
97
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
98
|
+
else:
|
99
|
+
headers["Authorization"] = f"Bearer None"
|
100
|
+
|
101
|
+
response = requests.get(url, headers=headers, timeout=20)
|
102
|
+
response.raise_for_status() # Raise an exception for bad responses
|
103
|
+
|
104
|
+
# Parse the data
|
105
|
+
data = response.json()
|
106
|
+
|
107
|
+
# Organize pricing data by (service, model) and token type
|
108
|
+
price_lookup = {}
|
109
|
+
for entry in data:
|
110
|
+
service = entry.get("service", None)
|
111
|
+
model = entry.get("model", None)
|
112
|
+
if service and model:
|
113
|
+
token_type = entry.get("token_type", None)
|
114
|
+
if (service, model) in price_lookup:
|
115
|
+
price_lookup[(service, model)].update({token_type: entry})
|
116
|
+
else:
|
117
|
+
price_lookup[(service, model)] = {token_type: entry}
|
118
|
+
|
119
|
+
# Cache and return the results
|
120
|
+
self._cached_prices = price_lookup
|
121
|
+
return self._cached_prices
|
122
|
+
|
123
|
+
except requests.RequestException as e:
|
124
|
+
# Silently handle errors and return empty dict
|
125
|
+
# print(f"An error occurred: {e}")
|
126
|
+
return {}
|
edsl/coop/utils.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
from typing import Literal, Optional, Type, Union
|
2
2
|
|
3
|
-
from
|
4
|
-
from
|
5
|
-
from
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from
|
10
|
-
from
|
11
|
-
|
12
|
-
from
|
13
|
-
|
14
|
-
from edsl.language_models.LanguageModel import LanguageModel
|
15
|
-
from edsl.questions.QuestionBase import QuestionBase
|
3
|
+
from ..agents import Agent, AgentList
|
4
|
+
from ..caching import Cache
|
5
|
+
from ..language_models import ModelList
|
6
|
+
from ..notebooks import Notebook
|
7
|
+
from ..results import Results
|
8
|
+
from ..scenarios import Scenario, ScenarioList
|
9
|
+
from ..surveys import Survey
|
10
|
+
from ..study import Study
|
11
|
+
|
12
|
+
from ..language_models import LanguageModel
|
13
|
+
from ..questions import QuestionBase
|
16
14
|
|
17
15
|
EDSLObject = Union[
|
18
16
|
Agent,
|
@@ -61,7 +59,23 @@ VisibilityType = Literal[
|
|
61
59
|
|
62
60
|
class ObjectRegistry:
|
63
61
|
"""
|
64
|
-
|
62
|
+
Registry that maps between EDSL class types and their cloud storage object types.
|
63
|
+
|
64
|
+
This utility class maintains a bidirectional mapping between EDSL Python classes
|
65
|
+
(like Survey, Agent, Results) and their corresponding object type identifiers
|
66
|
+
used in the cloud storage system. It enables the proper serialization,
|
67
|
+
deserialization, and type checking for objects stored in Expected Parrot's
|
68
|
+
cloud services.
|
69
|
+
|
70
|
+
The registry is used by the Coop client to:
|
71
|
+
1. Determine the correct object type when uploading EDSL objects
|
72
|
+
2. Instantiate the correct class when downloading objects
|
73
|
+
3. Validate that retrieved objects match expected types
|
74
|
+
|
75
|
+
Attributes:
|
76
|
+
objects (list): List of mappings between object types and EDSL classes
|
77
|
+
object_type_to_edsl_class (dict): Maps object type strings to EDSL classes
|
78
|
+
edsl_class_to_object_type (dict): Maps EDSL class names to object type strings
|
65
79
|
"""
|
66
80
|
|
67
81
|
objects = [
|
@@ -78,6 +92,8 @@ class ObjectRegistry:
|
|
78
92
|
{"object_type": "survey", "edsl_class": Survey},
|
79
93
|
{"object_type": "study", "edsl_class": Study},
|
80
94
|
]
|
95
|
+
|
96
|
+
# Create mappings for efficient lookups
|
81
97
|
object_type_to_edsl_class = {o["object_type"]: o["edsl_class"] for o in objects}
|
82
98
|
edsl_class_to_object_type = {
|
83
99
|
o["edsl_class"].__name__: o["object_type"] for o in objects
|
@@ -85,12 +101,36 @@ class ObjectRegistry:
|
|
85
101
|
|
86
102
|
@classmethod
|
87
103
|
def get_object_type_by_edsl_class(cls, edsl_object: EDSLObject) -> ObjectType:
|
104
|
+
"""
|
105
|
+
Get the object type identifier for an EDSL class or instance.
|
106
|
+
|
107
|
+
This method determines the appropriate object type string for a given EDSL class
|
108
|
+
or instance, which is needed when storing the object in the cloud.
|
109
|
+
|
110
|
+
Parameters:
|
111
|
+
edsl_object (EDSLObject): An EDSL class (type) or instance
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
ObjectType: The corresponding object type string (e.g., "survey", "agent")
|
115
|
+
|
116
|
+
Raises:
|
117
|
+
ValueError: If no mapping exists for the provided object
|
118
|
+
|
119
|
+
Notes:
|
120
|
+
- Special handling for Question classes, which all map to "question"
|
121
|
+
- Works with both class types and instances
|
122
|
+
"""
|
123
|
+
# Handle both class objects and instances
|
88
124
|
if isinstance(edsl_object, type):
|
89
125
|
edsl_class_name = edsl_object.__name__
|
90
126
|
else:
|
91
127
|
edsl_class_name = type(edsl_object).__name__
|
128
|
+
|
129
|
+
# Special handling for question classes
|
92
130
|
if edsl_class_name.startswith("Question"):
|
93
131
|
edsl_class_name = "QuestionBase"
|
132
|
+
|
133
|
+
# Look up the object type
|
94
134
|
object_type = cls.edsl_class_to_object_type.get(edsl_class_name)
|
95
135
|
if object_type is None:
|
96
136
|
raise ValueError(f"Object type not found for {edsl_object=}")
|
@@ -98,10 +138,25 @@ class ObjectRegistry:
|
|
98
138
|
|
99
139
|
@classmethod
|
100
140
|
def get_edsl_class_by_object_type(cls, object_type: ObjectType) -> EDSLObject:
|
101
|
-
|
102
|
-
|
141
|
+
"""
|
142
|
+
Get the EDSL class for a given object type identifier.
|
143
|
+
|
144
|
+
This method returns the appropriate EDSL class for a given object type string,
|
145
|
+
which is needed when retrieving objects from the cloud.
|
146
|
+
|
147
|
+
Parameters:
|
148
|
+
object_type (ObjectType): The object type string (e.g., "survey", "agent")
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
EDSLObject: The corresponding EDSL class
|
152
|
+
|
153
|
+
Raises:
|
154
|
+
ValueError: If no mapping exists for the provided object type
|
155
|
+
"""
|
156
|
+
EDSL_class = cls.object_type_to_edsl_class.get(object_type)
|
157
|
+
if EDSL_class is None:
|
103
158
|
raise ValueError(f"EDSL class not found for {object_type=}")
|
104
|
-
return
|
159
|
+
return EDSL_class
|
105
160
|
|
106
161
|
@classmethod
|
107
162
|
def get_registry(
|
@@ -110,14 +165,24 @@ class ObjectRegistry:
|
|
110
165
|
exclude_classes: Optional[list] = None,
|
111
166
|
) -> dict:
|
112
167
|
"""
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
168
|
+
Get a filtered registry of EDSL classes.
|
169
|
+
|
170
|
+
This method returns a dictionary of EDSL classes, optionally excluding
|
171
|
+
classes that are already registered elsewhere or explicitly excluded.
|
172
|
+
|
173
|
+
Parameters:
|
174
|
+
subclass_registry (dict, optional): Dictionary of classes to exclude
|
175
|
+
because they are already registered elsewhere
|
176
|
+
exclude_classes (list, optional): List of class names to explicitly exclude
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
dict: Dictionary mapping class names to EDSL classes
|
180
|
+
|
181
|
+
Notes:
|
182
|
+
- This method is useful for building registries of classes that
|
183
|
+
can be serialized and stored in the cloud
|
184
|
+
- It helps avoid duplicate registrations of classes
|
119
185
|
"""
|
120
|
-
|
121
186
|
if subclass_registry is None:
|
122
187
|
subclass_registry = {}
|
123
188
|
if exclude_classes is None:
|
edsl/data_transfer_models.py
CHANGED
@@ -1,74 +1,7 @@
|
|
1
|
-
|
2
|
-
from dataclasses import dataclass, fields
|
1
|
+
"""Data transfer models module wrapper for backward compatibility.
|
3
2
|
|
3
|
+
This module re-exports everything from the edsl.base.data_transfer_models module.
|
4
|
+
"""
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
user_prompt: str
|
8
|
-
system_prompt: str
|
9
|
-
encoded_image: Optional[str] = None
|
10
|
-
|
11
|
-
|
12
|
-
class EDSLOutput(NamedTuple):
|
13
|
-
"This is the edsl dictionary that is returned by the model"
|
14
|
-
answer: Any
|
15
|
-
generated_tokens: str
|
16
|
-
comment: Optional[str] = None
|
17
|
-
|
18
|
-
|
19
|
-
class ModelResponse(NamedTuple):
|
20
|
-
"This is the metadata that is returned by the model and includes info about the cache"
|
21
|
-
response: dict
|
22
|
-
cache_used: bool
|
23
|
-
cache_key: str
|
24
|
-
cached_response: Optional[Dict[str, Any]] = None
|
25
|
-
cost: Optional[float] = None
|
26
|
-
|
27
|
-
|
28
|
-
class AgentResponseDict(NamedTuple):
|
29
|
-
edsl_dict: EDSLOutput
|
30
|
-
model_inputs: ModelInputs
|
31
|
-
model_outputs: ModelResponse
|
32
|
-
|
33
|
-
|
34
|
-
class EDSLResultObjectInput(NamedTuple):
|
35
|
-
generated_tokens: str
|
36
|
-
question_name: str
|
37
|
-
prompts: dict
|
38
|
-
cached_response: str
|
39
|
-
raw_model_response: str
|
40
|
-
cache_used: bool
|
41
|
-
cache_key: str
|
42
|
-
answer: Any
|
43
|
-
comment: str
|
44
|
-
validated: bool = False
|
45
|
-
exception_occurred: Exception = None
|
46
|
-
cost: Optional[float] = None
|
47
|
-
|
48
|
-
|
49
|
-
@dataclass
|
50
|
-
class ImageInfo:
|
51
|
-
file_path: str
|
52
|
-
file_name: str
|
53
|
-
image_format: str
|
54
|
-
file_size: int
|
55
|
-
encoded_image: str
|
56
|
-
|
57
|
-
def __repr__(self):
|
58
|
-
import reprlib
|
59
|
-
|
60
|
-
reprlib_instance = reprlib.Repr()
|
61
|
-
reprlib_instance.maxstring = 30 # Limit the string length for the encoded image
|
62
|
-
|
63
|
-
# Get all fields except encoded_image
|
64
|
-
field_reprs = [
|
65
|
-
f"{f.name}={getattr(self, f.name)!r}"
|
66
|
-
for f in fields(self)
|
67
|
-
if f.name != "encoded_image"
|
68
|
-
]
|
69
|
-
|
70
|
-
# Add the reprlib-restricted encoded_image field
|
71
|
-
field_reprs.append(f"encoded_image={reprlib_instance.repr(self.encoded_image)}")
|
72
|
-
|
73
|
-
# Join everything to create the repr
|
74
|
-
return f"{self.__class__.__name__}({', '.join(field_reprs)})"
|
6
|
+
# Re-export everything from edsl.base.data_transfer_models module
|
7
|
+
from edsl.base.data_transfer_models import *
|
edsl/dataset/__init__.py
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
from .dataset import Dataset
|
2
|
+
|
3
|
+
from .dataset_operations_mixin import AgentListOperationsMixin
|
4
|
+
from .dataset_operations_mixin import ScenarioListOperationsMixin
|
5
|
+
from .dataset_operations_mixin import DatasetOperationsMixin
|
6
|
+
from .dataset_operations_mixin import ResultsOperationsMixin
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
"Dataset",
|
10
|
+
]
|