edsl 0.1.39.dev2__py3-none-any.whl → 0.1.39.dev3__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/Base.py +332 -385
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +49 -57
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +867 -1079
- edsl/agents/AgentList.py +413 -551
- edsl/agents/Invigilator.py +233 -285
- edsl/agents/InvigilatorBase.py +270 -254
- edsl/agents/PromptConstructor.py +354 -252
- edsl/agents/__init__.py +3 -2
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/auto/AutoStudy.py +117 -117
- edsl/auto/StageBase.py +230 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +73 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +224 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +157 -177
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +58 -59
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1028 -1090
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +555 -562
- edsl/data/CacheEntry.py +233 -230
- edsl/data/CacheHandler.py +149 -170
- edsl/data/RemoteCacheSync.py +78 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +4 -5
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +73 -74
- edsl/enums.py +175 -195
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +42 -54
- edsl/exceptions/cache.py +5 -5
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +91 -109
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +22 -29
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +87 -84
- edsl/inference_services/AwsBedrock.py +120 -118
- edsl/inference_services/AzureAI.py +217 -215
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +148 -139
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +147 -80
- edsl/inference_services/InferenceServicesCollection.py +97 -122
- edsl/inference_services/MistralAIService.py +123 -120
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +224 -221
- edsl/inference_services/PerplexityService.py +163 -160
- edsl/inference_services/TestService.py +89 -92
- edsl/inference_services/TogetherAIService.py +170 -170
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +41 -41
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/Answers.py +56 -43
- edsl/jobs/Jobs.py +898 -757
- edsl/jobs/JobsChecks.py +147 -172
- edsl/jobs/JobsPrompts.py +268 -270
- edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/buckets/BucketCollection.py +63 -104
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +251 -283
- edsl/jobs/interviews/Interview.py +661 -358
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
- edsl/jobs/runners/JobsRunnerStatus.py +330 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +450 -449
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +163 -161
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/KeyLookup.py +30 -0
- edsl/language_models/LanguageModel.py +668 -571
- edsl/language_models/ModelList.py +155 -153
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/__init__.py +3 -2
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/registry.py +190 -180
- edsl/language_models/repair.py +156 -156
- edsl/language_models/unused/ReplicateBase.py +83 -0
- edsl/language_models/utilities.py +64 -65
- edsl/notebooks/Notebook.py +258 -263
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +362 -352
- edsl/prompts/__init__.py +2 -2
- edsl/questions/AnswerValidatorMixin.py +289 -334
- edsl/questions/QuestionBase.py +664 -509
- edsl/questions/QuestionBaseGenMixin.py +161 -165
- edsl/questions/QuestionBasePromptsMixin.py +217 -221
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +182 -182
- edsl/questions/QuestionFreeText.py +114 -113
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +231 -229
- edsl/questions/QuestionMultipleChoice.py +286 -330
- edsl/questions/QuestionNumerical.py +153 -151
- edsl/questions/QuestionRank.py +324 -314
- edsl/questions/Quick.py +41 -41
- edsl/questions/RegisterQuestionsMeta.py +71 -71
- edsl/questions/ResponseValidatorABC.py +174 -200
- edsl/questions/SimpleAskMixin.py +73 -74
- edsl/questions/__init__.py +26 -27
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +87 -90
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +413 -427
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/question_registry.py +177 -177
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/CSSParameterizer.py +108 -108
- edsl/results/Dataset.py +424 -587
- edsl/results/DatasetExportMixin.py +731 -653
- edsl/results/DatasetTree.py +275 -295
- edsl/results/Result.py +465 -451
- edsl/results/Results.py +1165 -1172
- edsl/results/ResultsDBMixin.py +238 -0
- edsl/results/ResultsExportMixin.py +43 -45
- edsl/results/ResultsFetchMixin.py +33 -33
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/ResultsToolsMixin.py +98 -98
- edsl/results/Selector.py +135 -145
- edsl/results/TableDisplay.py +198 -125
- edsl/results/__init__.py +2 -2
- edsl/results/table_display.css +77 -77
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/FileStore.py +632 -511
- edsl/scenarios/Scenario.py +601 -498
- edsl/scenarios/ScenarioHtmlMixin.py +64 -65
- edsl/scenarios/ScenarioJoin.py +127 -131
- edsl/scenarios/ScenarioList.py +1287 -1430
- edsl/scenarios/ScenarioListExportMixin.py +52 -45
- edsl/scenarios/ScenarioListPdfMixin.py +261 -239
- edsl/scenarios/__init__.py +4 -3
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +528 -521
- edsl/study/__init__.py +4 -4
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +326 -327
- edsl/surveys/RuleCollection.py +387 -385
- edsl/surveys/Survey.py +1801 -1229
- edsl/surveys/SurveyCSS.py +261 -273
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/__init__.py +3 -5
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +56 -60
- edsl/surveys/instructions/ChangeInstruction.py +49 -48
- edsl/surveys/instructions/Instruction.py +65 -56
- edsl/surveys/instructions/InstructionCollection.py +77 -82
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +19 -19
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +424 -436
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
- edsl-0.1.39.dev3.dist-info/RECORD +277 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
- edsl/agents/QuestionOptionProcessor.py +0 -172
- edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
- edsl/coop/CoopFunctionsMixin.py +0 -15
- edsl/coop/ExpectedParrotKeyHandler.py +0 -125
- edsl/exceptions/inference_services.py +0 -5
- edsl/inference_services/AvailableModelCacheHandler.py +0 -184
- edsl/inference_services/AvailableModelFetcher.py +0 -209
- edsl/inference_services/ServiceAvailability.py +0 -135
- edsl/inference_services/data_structures.py +0 -62
- edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
- edsl/jobs/FetchInvigilator.py +0 -40
- edsl/jobs/InterviewTaskManager.py +0 -98
- edsl/jobs/InterviewsConstructor.py +0 -48
- edsl/jobs/JobsComponentConstructor.py +0 -189
- edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
- edsl/jobs/RequestTokenEstimator.py +0 -30
- edsl/jobs/buckets/TokenBucketAPI.py +0 -211
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/decorators.py +0 -35
- edsl/jobs/jobs_status_enums.py +0 -9
- edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
- edsl/language_models/ComputeCost.py +0 -63
- edsl/language_models/PriceManager.py +0 -127
- edsl/language_models/RawResponseHandler.py +0 -106
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +0 -131
- edsl/notebooks/NotebookToLaTeX.py +0 -142
- edsl/questions/ExceptionExplainer.py +0 -77
- edsl/questions/HTMLQuestion.py +0 -103
- edsl/questions/LoopProcessor.py +0 -149
- edsl/questions/QuestionMatrix.py +0 -265
- edsl/questions/ResponseValidatorFactory.py +0 -28
- edsl/questions/templates/matrix/__init__.py +0 -1
- edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
- edsl/questions/templates/matrix/question_presentation.jinja +0 -20
- edsl/results/MarkdownToDocx.py +0 -122
- edsl/results/MarkdownToPDF.py +0 -111
- edsl/results/TextEditor.py +0 -50
- edsl/results/smart_objects.py +0 -96
- edsl/results/table_data_class.py +0 -12
- edsl/results/table_renderers.py +0 -118
- edsl/scenarios/ConstructDownloadLink.py +0 -109
- edsl/scenarios/DirectoryScanner.py +0 -96
- edsl/scenarios/DocumentChunker.py +0 -102
- edsl/scenarios/DocxScenario.py +0 -16
- edsl/scenarios/PdfExtractor.py +0 -40
- edsl/scenarios/ScenarioSelector.py +0 -156
- edsl/scenarios/file_methods.py +0 -85
- edsl/scenarios/handlers/__init__.py +0 -13
- edsl/scenarios/handlers/csv.py +0 -38
- edsl/scenarios/handlers/docx.py +0 -76
- edsl/scenarios/handlers/html.py +0 -37
- edsl/scenarios/handlers/json.py +0 -111
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/scenarios/handlers/md.py +0 -51
- edsl/scenarios/handlers/pdf.py +0 -68
- edsl/scenarios/handlers/png.py +0 -39
- edsl/scenarios/handlers/pptx.py +0 -105
- edsl/scenarios/handlers/py.py +0 -294
- edsl/scenarios/handlers/sql.py +0 -313
- edsl/scenarios/handlers/sqlite.py +0 -149
- edsl/scenarios/handlers/txt.py +0 -33
- edsl/surveys/ConstructDAG.py +0 -92
- edsl/surveys/EditSurvey.py +0 -221
- edsl/surveys/InstructionHandler.py +0 -100
- edsl/surveys/MemoryManagement.py +0 -72
- edsl/surveys/RuleManager.py +0 -172
- edsl/surveys/Simulator.py +0 -75
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/utilities/PrettyList.py +0 -56
- edsl/utilities/is_notebook.py +0 -18
- edsl/utilities/is_valid_variable_name.py +0 -11
- edsl/utilities/remove_edsl_version.py +0 -24
- edsl-0.1.39.dev2.dist-info/RECORD +0 -352
- {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/WHEEL +0 -0
@@ -1,273 +0,0 @@
|
|
1
|
-
from typing import Optional, List
|
2
|
-
from collections import UserDict
|
3
|
-
import os
|
4
|
-
from functools import lru_cache
|
5
|
-
from dataclasses import dataclass, asdict
|
6
|
-
|
7
|
-
from edsl.enums import service_to_api_keyname
|
8
|
-
from edsl.exceptions.general import MissingAPIKeyError
|
9
|
-
|
10
|
-
from edsl.language_models.key_management.KeyLookup import KeyLookup
|
11
|
-
|
12
|
-
from edsl.language_models.key_management.models import (
|
13
|
-
APIKeyEntry,
|
14
|
-
LimitEntry,
|
15
|
-
APIIDEntry,
|
16
|
-
LanguageModelInput,
|
17
|
-
)
|
18
|
-
|
19
|
-
service_to_api_keyname["bedrock"] = "AWS_SECRET_ACCESS_KEY"
|
20
|
-
service_to_api_id = {"bedrock": "AWS_ACCESS_KEY_ID"}
|
21
|
-
|
22
|
-
api_keyname_to_service = {}
|
23
|
-
|
24
|
-
for service, key in service_to_api_keyname.items():
|
25
|
-
if isinstance(key, list):
|
26
|
-
for k in key:
|
27
|
-
api_keyname_to_service[k] = service
|
28
|
-
else:
|
29
|
-
api_keyname_to_service[key] = service
|
30
|
-
|
31
|
-
api_id_to_service = {"AWS_ACCESS_KEY_ID": "bedrock"}
|
32
|
-
|
33
|
-
|
34
|
-
class KeyLookupBuilder:
|
35
|
-
"""Builds KeyLookup options.
|
36
|
-
|
37
|
-
>>> builder = KeyLookupBuilder(fetch_order=("config", "env"))
|
38
|
-
>>> builder.DEFAULT_RPM
|
39
|
-
10
|
40
|
-
>>> builder.DEFAULT_TPM
|
41
|
-
2000000
|
42
|
-
>>> builder.fetch_order
|
43
|
-
('config', 'env')
|
44
|
-
|
45
|
-
Test invalid fetch_order:
|
46
|
-
>>> try:
|
47
|
-
... KeyLookupBuilder(fetch_order=["config", "env"]) # Should be tuple
|
48
|
-
... except ValueError as e:
|
49
|
-
... str(e)
|
50
|
-
'fetch_order must be a tuple'
|
51
|
-
|
52
|
-
Test service extraction:
|
53
|
-
>>> builder.extract_service("EDSL_SERVICE_RPM_OPENAI")
|
54
|
-
('openai', 'rpm')
|
55
|
-
"""
|
56
|
-
|
57
|
-
DEFAULT_RPM = 10
|
58
|
-
DEFAULT_TPM = 2000000
|
59
|
-
|
60
|
-
def __init__(self, fetch_order: Optional[tuple[str]] = None):
|
61
|
-
if fetch_order is None:
|
62
|
-
self.fetch_order = ("config", "env")
|
63
|
-
else:
|
64
|
-
self.fetch_order = fetch_order
|
65
|
-
|
66
|
-
if not isinstance(self.fetch_order, tuple):
|
67
|
-
raise ValueError("fetch_order must be a tuple")
|
68
|
-
|
69
|
-
self.limit_data = {}
|
70
|
-
self.key_data = {}
|
71
|
-
self.id_data = {}
|
72
|
-
self.process_key_value_pairs()
|
73
|
-
|
74
|
-
@property
|
75
|
-
def known_services(self):
|
76
|
-
"""Get the set of known services.
|
77
|
-
|
78
|
-
>>> builder = KeyLookupBuilder()
|
79
|
-
>>> isinstance(builder.known_services, set)
|
80
|
-
True
|
81
|
-
"""
|
82
|
-
return set(self.key_data.keys()) | set(self.limit_data.keys())
|
83
|
-
|
84
|
-
@lru_cache
|
85
|
-
def build(self) -> "KeyLookup":
|
86
|
-
"""Build a KeyLookup instance.
|
87
|
-
|
88
|
-
>>> builder = KeyLookupBuilder()
|
89
|
-
>>> lookup = builder.build()
|
90
|
-
>>> isinstance(lookup, KeyLookup)
|
91
|
-
True
|
92
|
-
>>> lookup['test'].api_token # Test service should always exist
|
93
|
-
'test'
|
94
|
-
"""
|
95
|
-
d = {}
|
96
|
-
for service in self.known_services:
|
97
|
-
try:
|
98
|
-
d[service] = self.get_language_model_input(service)
|
99
|
-
except MissingAPIKeyError:
|
100
|
-
pass
|
101
|
-
|
102
|
-
d.update({"test": LanguageModelInput(api_token="test", rpm=10, tpm=2000000)})
|
103
|
-
return KeyLookup(d)
|
104
|
-
|
105
|
-
def get_language_model_input(self, service: str) -> LanguageModelInput:
|
106
|
-
"""Get the language model input for a given service.
|
107
|
-
|
108
|
-
>>> builder = KeyLookupBuilder()
|
109
|
-
>>> try:
|
110
|
-
... builder.get_language_model_input("nonexistent_service")
|
111
|
-
... except MissingAPIKeyError as e:
|
112
|
-
... str(e)
|
113
|
-
"No key found for service 'nonexistent_service'"
|
114
|
-
"""
|
115
|
-
if (key_entries := self.key_data.get(service)) is None:
|
116
|
-
raise MissingAPIKeyError(f"No key found for service '{service}'")
|
117
|
-
|
118
|
-
if len(key_entries) == 1:
|
119
|
-
api_key_entry = key_entries[0]
|
120
|
-
|
121
|
-
id_entry = self.id_data.get(service)
|
122
|
-
id_source = id_entry.source if id_entry is not None else None
|
123
|
-
api_id = id_entry.value if id_entry is not None else None
|
124
|
-
|
125
|
-
if (limit_entry := self.limit_data.get(service)) is None:
|
126
|
-
limit_entry = LimitEntry(
|
127
|
-
service=service,
|
128
|
-
rpm=self.DEFAULT_RPM,
|
129
|
-
tpm=self.DEFAULT_TPM,
|
130
|
-
source="default",
|
131
|
-
)
|
132
|
-
|
133
|
-
if limit_entry.rpm is None:
|
134
|
-
limit_entry.rpm = self.DEFAULT_RPM
|
135
|
-
if limit_entry.tpm is None:
|
136
|
-
limit_entry.tpm = self.DEFAULT_TPM
|
137
|
-
|
138
|
-
return LanguageModelInput(
|
139
|
-
api_token=api_key_entry.value,
|
140
|
-
rpm=int(limit_entry.rpm),
|
141
|
-
tpm=int(limit_entry.tpm),
|
142
|
-
api_id=api_id,
|
143
|
-
token_source=api_key_entry.source,
|
144
|
-
limit_source=limit_entry.source,
|
145
|
-
id_source=id_source,
|
146
|
-
)
|
147
|
-
|
148
|
-
def __repr__(self):
|
149
|
-
return f"DataSource(key_data={self.key_data}, limit_data={self.limit_data}, id_data={self.id_data})"
|
150
|
-
|
151
|
-
def _os_env_key_value_pairs(self):
|
152
|
-
return dict(list(os.environ.items()))
|
153
|
-
|
154
|
-
def _coop_key_value_pairs(self):
|
155
|
-
from edsl.coop import Coop
|
156
|
-
|
157
|
-
c = Coop()
|
158
|
-
return dict(list(c.fetch_rate_limit_config_vars().items()))
|
159
|
-
|
160
|
-
def _config_key_value_pairs(self):
|
161
|
-
from edsl.config import CONFIG
|
162
|
-
|
163
|
-
return dict(list(CONFIG.items()))
|
164
|
-
|
165
|
-
@staticmethod
|
166
|
-
def extract_service(key: str) -> str:
|
167
|
-
"""Extract the service and limit type from the key"""
|
168
|
-
limit_type, service_raw = key.replace("EDSL_SERVICE_", "").split("_")
|
169
|
-
return service_raw.lower(), limit_type.lower()
|
170
|
-
|
171
|
-
def get_key_value_pairs(self) -> dict:
|
172
|
-
"""Get key-value pairs from configured sources."""
|
173
|
-
fetching_functions = {
|
174
|
-
"env": self._os_env_key_value_pairs,
|
175
|
-
"coop": self._coop_key_value_pairs,
|
176
|
-
"config": self._config_key_value_pairs,
|
177
|
-
}
|
178
|
-
d = {}
|
179
|
-
for source in self.fetch_order:
|
180
|
-
f = fetching_functions[source]
|
181
|
-
new_data = f()
|
182
|
-
for k, v in new_data.items():
|
183
|
-
d[k] = (v, source)
|
184
|
-
return d
|
185
|
-
|
186
|
-
def _entry_type(self, key, value) -> str:
|
187
|
-
"""Determine the type of entry from a key.
|
188
|
-
|
189
|
-
>>> builder = KeyLookupBuilder()
|
190
|
-
>>> builder._entry_type("EDSL_SERVICE_RPM_OPENAI", "60")
|
191
|
-
'limit'
|
192
|
-
>>> builder._entry_type("OPENAI_API_KEY", "sk-1234")
|
193
|
-
'api_key'
|
194
|
-
>>> builder._entry_type("AWS_ACCESS_KEY_ID", "AKIA1234")
|
195
|
-
'api_id'
|
196
|
-
>>> builder._entry_type("UNKNOWN_KEY", "value")
|
197
|
-
'unknown'
|
198
|
-
"""
|
199
|
-
if key.startswith("EDSL_SERVICE_"):
|
200
|
-
return "limit"
|
201
|
-
elif key in api_keyname_to_service:
|
202
|
-
return "api_key"
|
203
|
-
elif key in api_id_to_service:
|
204
|
-
return "api_id"
|
205
|
-
return "unknown"
|
206
|
-
|
207
|
-
def _add_id(self, key: str, value: str, source: str) -> None:
|
208
|
-
"""Add an API ID to the id_data dictionary.
|
209
|
-
|
210
|
-
>>> builder = KeyLookupBuilder()
|
211
|
-
>>> builder._add_id("AWS_ACCESS_KEY_ID", "AKIA1234", "env")
|
212
|
-
>>> builder.id_data["bedrock"].value
|
213
|
-
'AKIA1234'
|
214
|
-
>>> try:
|
215
|
-
... builder._add_id("AWS_ACCESS_KEY_ID", "AKIA5678", "env")
|
216
|
-
... except ValueError as e:
|
217
|
-
... str(e)
|
218
|
-
'Duplicate ID for service bedrock'
|
219
|
-
"""
|
220
|
-
service = api_id_to_service[key]
|
221
|
-
if service not in self.id_data:
|
222
|
-
self.id_data[service] = APIIDEntry(
|
223
|
-
service=service, name=key, value=value, source=source
|
224
|
-
)
|
225
|
-
else:
|
226
|
-
raise ValueError(f"Duplicate ID for service {service}")
|
227
|
-
|
228
|
-
def _add_limit(self, key: str, value: str, source: str) -> None:
|
229
|
-
"""Add a rate limit entry to the limit_data dictionary.
|
230
|
-
|
231
|
-
>>> builder = KeyLookupBuilder()
|
232
|
-
>>> builder._add_limit("EDSL_SERVICE_RPM_OPENAI", "60", "config")
|
233
|
-
>>> builder.limit_data["openai"].rpm
|
234
|
-
'60'
|
235
|
-
>>> builder._add_limit("EDSL_SERVICE_TPM_OPENAI", "100000", "config")
|
236
|
-
>>> builder.limit_data["openai"].tpm
|
237
|
-
'100000'
|
238
|
-
"""
|
239
|
-
service, limit_type = self.extract_service(key)
|
240
|
-
if service in self.limit_data:
|
241
|
-
setattr(self.limit_data[service], limit_type.lower(), value)
|
242
|
-
else:
|
243
|
-
new_limit_entry = LimitEntry(
|
244
|
-
service=service, rpm=None, tpm=None, source=source
|
245
|
-
)
|
246
|
-
setattr(new_limit_entry, limit_type.lower(), value)
|
247
|
-
self.limit_data[service] = new_limit_entry
|
248
|
-
|
249
|
-
def _add_api_key(self, key: str, value: str, source: str) -> None:
|
250
|
-
"""Add an API key entry to the key_data dictionary.
|
251
|
-
|
252
|
-
>>> builder = KeyLookupBuilder()
|
253
|
-
>>> builder._add_api_key("OPENAI_API_KEY", "sk-1234", "env")
|
254
|
-
>>> 'sk-1234' == builder.key_data["openai"][-1].value
|
255
|
-
True
|
256
|
-
"""
|
257
|
-
service = api_keyname_to_service[key]
|
258
|
-
new_entry = APIKeyEntry(service=service, name=key, value=value, source=source)
|
259
|
-
if service not in self.key_data:
|
260
|
-
self.key_data[service] = [new_entry]
|
261
|
-
else:
|
262
|
-
self.key_data[service].append(new_entry)
|
263
|
-
|
264
|
-
def process_key_value_pairs(self) -> None:
|
265
|
-
"""Process all key-value pairs from the configured sources."""
|
266
|
-
for key, value_pair in self.get_key_value_pairs().items():
|
267
|
-
value, source = value_pair
|
268
|
-
if (entry_type := self._entry_type(key, value)) == "limit":
|
269
|
-
self._add_limit(key, value, source)
|
270
|
-
elif entry_type == "api_key":
|
271
|
-
self._add_api_key(key, value, source)
|
272
|
-
elif entry_type == "api_id":
|
273
|
-
self._add_id(key, value, source)
|
@@ -1,38 +0,0 @@
|
|
1
|
-
from collections import UserDict
|
2
|
-
|
3
|
-
from edsl.language_models.key_management.KeyLookupBuilder import KeyLookupBuilder
|
4
|
-
|
5
|
-
|
6
|
-
class KeyLookupCollection(UserDict):
|
7
|
-
"""A singleton class that stores key-lookup objects.
|
8
|
-
|
9
|
-
This is because once a KeyLook is created once, we do not
|
10
|
-
need to keep re-creating it.
|
11
|
-
|
12
|
-
>>> collection = KeyLookupCollection()
|
13
|
-
>>> collection2 = KeyLookupCollection()
|
14
|
-
>>> collection is collection2 # Test singleton pattern
|
15
|
-
True
|
16
|
-
>>> collection.add_key_lookup(("config", "env"))
|
17
|
-
>>> ("config", "env") in collection.data
|
18
|
-
True
|
19
|
-
"""
|
20
|
-
|
21
|
-
_instance = None
|
22
|
-
|
23
|
-
def __new__(cls, *args, **kwargs):
|
24
|
-
if cls._instance is None:
|
25
|
-
cls._instance = super().__new__(cls)
|
26
|
-
return cls._instance
|
27
|
-
|
28
|
-
def __init__(self, *args, **kwargs):
|
29
|
-
if not hasattr(self, "_initialized"):
|
30
|
-
self.data = {}
|
31
|
-
self._initialized = True
|
32
|
-
super().__init__(*args, **kwargs)
|
33
|
-
|
34
|
-
def add_key_lookup(self, fetch_order=None):
|
35
|
-
if fetch_order is None:
|
36
|
-
fetch_order = ("config", "env")
|
37
|
-
if fetch_order not in self.data:
|
38
|
-
self.data[fetch_order] = KeyLookupBuilder(fetch_order=fetch_order).build()
|
File without changes
|
@@ -1,131 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass, asdict
|
2
|
-
from typing import Optional
|
3
|
-
|
4
|
-
|
5
|
-
@dataclass
|
6
|
-
class APIKeyEntry:
|
7
|
-
"""A class representing an API key entry.
|
8
|
-
|
9
|
-
>>> entry = APIKeyEntry.example()
|
10
|
-
>>> entry.service
|
11
|
-
'openai'
|
12
|
-
>>> entry.name
|
13
|
-
'OPENAI_API_KEY'
|
14
|
-
>>> entry.value
|
15
|
-
'sk-abcd1234'
|
16
|
-
>>> entry.source
|
17
|
-
'env'
|
18
|
-
"""
|
19
|
-
|
20
|
-
service: str
|
21
|
-
name: str
|
22
|
-
value: str
|
23
|
-
source: Optional[str] = None
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
def example(cls):
|
27
|
-
return APIKeyEntry(
|
28
|
-
service="openai", name="OPENAI_API_KEY", value="sk-abcd1234", source="env"
|
29
|
-
)
|
30
|
-
|
31
|
-
|
32
|
-
@dataclass
|
33
|
-
class LimitEntry:
|
34
|
-
"""A class representing rate limit entries for a service.
|
35
|
-
|
36
|
-
>>> limit = LimitEntry.example()
|
37
|
-
>>> limit.service
|
38
|
-
'openai'
|
39
|
-
>>> limit.rpm
|
40
|
-
60
|
41
|
-
>>> limit.tpm
|
42
|
-
100000
|
43
|
-
>>> limit.source
|
44
|
-
'config'
|
45
|
-
"""
|
46
|
-
|
47
|
-
service: str
|
48
|
-
rpm: int
|
49
|
-
tpm: int
|
50
|
-
source: Optional[str] = None
|
51
|
-
|
52
|
-
@classmethod
|
53
|
-
def example(cls):
|
54
|
-
return LimitEntry(service="openai", rpm=60, tpm=100000, source="config")
|
55
|
-
|
56
|
-
|
57
|
-
@dataclass
|
58
|
-
class APIIDEntry:
|
59
|
-
"""A class representing an API ID entry.
|
60
|
-
|
61
|
-
>>> id_entry = APIIDEntry.example()
|
62
|
-
>>> id_entry.service
|
63
|
-
'bedrock'
|
64
|
-
>>> id_entry.name
|
65
|
-
'AWS_ACCESS_KEY_ID'
|
66
|
-
>>> id_entry.value
|
67
|
-
'AKIA1234'
|
68
|
-
>>> id_entry.source
|
69
|
-
'env'
|
70
|
-
"""
|
71
|
-
|
72
|
-
service: str
|
73
|
-
name: str
|
74
|
-
value: str
|
75
|
-
source: Optional[str] = None
|
76
|
-
|
77
|
-
@classmethod
|
78
|
-
def example(cls):
|
79
|
-
return APIIDEntry(
|
80
|
-
service="bedrock", name="AWS_ACCESS_KEY_ID", value="AKIA1234", source="env"
|
81
|
-
)
|
82
|
-
|
83
|
-
|
84
|
-
@dataclass
|
85
|
-
class LanguageModelInput:
|
86
|
-
"""A class representing input configuration for a language model service.
|
87
|
-
|
88
|
-
>>> lm_input = LanguageModelInput.example()
|
89
|
-
>>> lm_input.api_token
|
90
|
-
'sk-abcd123'
|
91
|
-
>>> lm_input.rpm
|
92
|
-
60
|
93
|
-
>>> lm_input.tpm
|
94
|
-
100000
|
95
|
-
>>> lm_input.api_id
|
96
|
-
|
97
|
-
|
98
|
-
Test dictionary conversion:
|
99
|
-
>>> d = lm_input.to_dict()
|
100
|
-
>>> isinstance(d, dict)
|
101
|
-
True
|
102
|
-
>>> LanguageModelInput.from_dict(d).api_token == lm_input.api_token
|
103
|
-
True
|
104
|
-
"""
|
105
|
-
|
106
|
-
api_token: str
|
107
|
-
rpm: int
|
108
|
-
tpm: int
|
109
|
-
api_id: Optional[str] = None
|
110
|
-
token_source: Optional[str] = None
|
111
|
-
limit_source: Optional[str] = None
|
112
|
-
id_source: Optional[str] = None
|
113
|
-
|
114
|
-
def to_dict(self):
|
115
|
-
return asdict(self)
|
116
|
-
|
117
|
-
@classmethod
|
118
|
-
def from_dict(cls, d):
|
119
|
-
return cls(**d)
|
120
|
-
|
121
|
-
@classmethod
|
122
|
-
def example(cls):
|
123
|
-
return LanguageModelInput(
|
124
|
-
api_token="sk-abcd123", tpm=100000, rpm=60, api_id=None
|
125
|
-
)
|
126
|
-
|
127
|
-
|
128
|
-
if __name__ == "__main__":
|
129
|
-
import doctest
|
130
|
-
|
131
|
-
doctest.testmod()
|
@@ -1,142 +0,0 @@
|
|
1
|
-
from typing import Optional, Dict
|
2
|
-
import os
|
3
|
-
import nbformat
|
4
|
-
from nbconvert.exporters import LatexExporter
|
5
|
-
from nbconvert.writers import FilesWriter
|
6
|
-
|
7
|
-
|
8
|
-
class NotebookToLaTeX:
|
9
|
-
"""
|
10
|
-
A class for converting Jupyter notebooks to LaTeX with proper directory structure.
|
11
|
-
"""
|
12
|
-
|
13
|
-
def __init__(self, notebook):
|
14
|
-
"""
|
15
|
-
Initialize with a Notebook instance.
|
16
|
-
|
17
|
-
:param notebook: An instance of the Notebook class
|
18
|
-
"""
|
19
|
-
self.notebook = notebook
|
20
|
-
self.latex_exporter = LatexExporter()
|
21
|
-
self._configure_exporter()
|
22
|
-
|
23
|
-
def _configure_exporter(self):
|
24
|
-
"""Configure the LaTeX exporter with default settings."""
|
25
|
-
self.latex_exporter.exclude_input_prompt = True
|
26
|
-
self.latex_exporter.exclude_output_prompt = True
|
27
|
-
self.latex_exporter.template_name = "classic"
|
28
|
-
|
29
|
-
def _create_makefile(self, filename: str, output_dir: str):
|
30
|
-
"""Create a Makefile for the LaTeX project."""
|
31
|
-
makefile_content = f"""# Makefile for {filename}
|
32
|
-
all: pdf
|
33
|
-
|
34
|
-
pdf: {filename}.pdf
|
35
|
-
|
36
|
-
{filename}.pdf: {filename}.tex
|
37
|
-
\tpdflatex {filename}.tex
|
38
|
-
\tpdflatex {filename}.tex # Run twice for references
|
39
|
-
\tbibtex {filename} # Run bibtex if needed
|
40
|
-
\tpdflatex {filename}.tex # Run one more time for bibtex
|
41
|
-
|
42
|
-
clean:
|
43
|
-
\trm -f *.aux *.log *.out *.toc *.pdf *.bbl *.blg
|
44
|
-
"""
|
45
|
-
makefile_path = os.path.join(output_dir, "Makefile")
|
46
|
-
with open(makefile_path, "w") as f:
|
47
|
-
f.write(makefile_content)
|
48
|
-
|
49
|
-
def _create_readme(self, filename: str, output_dir: str):
|
50
|
-
"""Create a README file with usage instructions."""
|
51
|
-
readme_content = f"""# {filename}
|
52
|
-
|
53
|
-
This folder contains the LaTeX version of your Jupyter notebook.
|
54
|
-
|
55
|
-
Files:
|
56
|
-
- {filename}.tex: Main LaTeX file
|
57
|
-
- Makefile: Build automation
|
58
|
-
|
59
|
-
To compile the PDF:
|
60
|
-
1. Make sure you have a LaTeX distribution installed (e.g., TexLive)
|
61
|
-
2. Run `make` in this directory
|
62
|
-
3. The output will be {filename}.pdf
|
63
|
-
|
64
|
-
To clean up build files:
|
65
|
-
- Run `make clean`
|
66
|
-
"""
|
67
|
-
readme_path = os.path.join(output_dir, "README.md")
|
68
|
-
with open(readme_path, "w") as f:
|
69
|
-
f.write(readme_content)
|
70
|
-
|
71
|
-
def convert(self, filename: str, output_dir: Optional[str] = None):
|
72
|
-
"""
|
73
|
-
Convert the notebook to LaTeX and create a project directory.
|
74
|
-
|
75
|
-
:param filename: Name for the output files (without extension)
|
76
|
-
:param output_dir: Optional directory path. If None, uses filename as directory
|
77
|
-
"""
|
78
|
-
# Use filename as directory if no output_dir specified
|
79
|
-
output_dir = output_dir or filename
|
80
|
-
|
81
|
-
# Create output directory
|
82
|
-
os.makedirs(output_dir, exist_ok=True)
|
83
|
-
|
84
|
-
# Convert notebook to nbformat
|
85
|
-
notebook_node = nbformat.from_dict(self.notebook.data)
|
86
|
-
|
87
|
-
# Convert to LaTeX
|
88
|
-
body, resources = self.latex_exporter.from_notebook_node(notebook_node)
|
89
|
-
|
90
|
-
# Write the main tex file
|
91
|
-
output_file_path = os.path.join(output_dir, f"{filename}.tex")
|
92
|
-
with open(output_file_path, "w", encoding="utf-8") as f:
|
93
|
-
f.write(body)
|
94
|
-
|
95
|
-
# Write additional resources (images, etc.)
|
96
|
-
if resources.get("outputs"):
|
97
|
-
for fname, data in resources["outputs"].items():
|
98
|
-
resource_path = os.path.join(output_dir, fname)
|
99
|
-
with open(resource_path, "wb") as f:
|
100
|
-
f.write(data)
|
101
|
-
|
102
|
-
# Create supporting files
|
103
|
-
self._create_makefile(filename, output_dir)
|
104
|
-
self._create_readme(filename, output_dir)
|
105
|
-
|
106
|
-
def set_template(self, template_name: str):
|
107
|
-
"""
|
108
|
-
Set the LaTeX template to use.
|
109
|
-
|
110
|
-
:param template_name: Name of the template (e.g., 'classic', 'article')
|
111
|
-
"""
|
112
|
-
self.latex_exporter.template_name = template_name
|
113
|
-
|
114
|
-
def set_template_options(self, options: Dict):
|
115
|
-
"""
|
116
|
-
Set additional template options.
|
117
|
-
|
118
|
-
:param options: Dictionary of template options
|
119
|
-
"""
|
120
|
-
for key, value in options.items():
|
121
|
-
setattr(self.latex_exporter, key, value)
|
122
|
-
|
123
|
-
|
124
|
-
# Example usage:
|
125
|
-
if __name__ == "__main__":
|
126
|
-
from edsl import Notebook
|
127
|
-
|
128
|
-
# Create or load a notebook
|
129
|
-
notebook = Notebook.example()
|
130
|
-
|
131
|
-
# Create converter and convert
|
132
|
-
converter = NotebookToLaTeX(notebook)
|
133
|
-
converter.convert("example_output")
|
134
|
-
|
135
|
-
# Example with custom template options
|
136
|
-
converter.set_template_options(
|
137
|
-
{
|
138
|
-
"exclude_input": True, # Hide input cells
|
139
|
-
"exclude_output": False, # Show output cells
|
140
|
-
}
|
141
|
-
)
|
142
|
-
converter.convert("example_output_custom")
|
@@ -1,77 +0,0 @@
|
|
1
|
-
from pydantic import ValidationError
|
2
|
-
from typing import Union
|
3
|
-
|
4
|
-
|
5
|
-
class ExceptionExplainer:
|
6
|
-
"""
|
7
|
-
A class that converts validation errors into human-readable explanations,
|
8
|
-
specifically for Language Model responses.
|
9
|
-
"""
|
10
|
-
|
11
|
-
def __init__(self, error: Union[ValidationError, Exception], model_response: str):
|
12
|
-
"""
|
13
|
-
Initialize the explainer with the error and model response.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
error: The validation error that occurred
|
17
|
-
model_response: The raw response from the Language Model
|
18
|
-
"""
|
19
|
-
self.error = error
|
20
|
-
self.model_response = model_response
|
21
|
-
|
22
|
-
def explain(self) -> str:
|
23
|
-
"""
|
24
|
-
Generate a human-readable explanation of why the model's response failed validation.
|
25
|
-
|
26
|
-
Returns:
|
27
|
-
A user-friendly explanation of why the model's response was invalid
|
28
|
-
"""
|
29
|
-
self.error = self.error.pydantic_error
|
30
|
-
return self._explain_validation_error()
|
31
|
-
|
32
|
-
# Fallback for unknown errors
|
33
|
-
return self._create_generic_explanation()
|
34
|
-
|
35
|
-
def _explain_validation_error(self) -> str:
|
36
|
-
"""Handle Pydantic ValidationError specifically."""
|
37
|
-
error_dict = self.error.errors()
|
38
|
-
explanations = []
|
39
|
-
|
40
|
-
context = f'The AI model returned "{self.model_response}", but this was invalid for the question you asked and the constraints you provided.\n'
|
41
|
-
explanations.append(context)
|
42
|
-
explanations.append("Reason(s) invalidated:")
|
43
|
-
for e in error_dict:
|
44
|
-
msg = e.get("msg", "Unknown error")
|
45
|
-
explanations.append(f"- {msg}")
|
46
|
-
|
47
|
-
main_message = "\n".join(explanations)
|
48
|
-
return f"{main_message}\n\n{self._get_suggestion()}"
|
49
|
-
|
50
|
-
def _create_generic_explanation(self) -> str:
|
51
|
-
"""Create a generic explanation for non-ValidationError exceptions."""
|
52
|
-
return (
|
53
|
-
f'The AI model returned "{self.model_response}", but this response was invalid. '
|
54
|
-
f"Error: {str(self.error)}"
|
55
|
-
)
|
56
|
-
|
57
|
-
def _get_suggestion(self) -> str:
|
58
|
-
"""Get a suggestion for handling the error."""
|
59
|
-
return (
|
60
|
-
"EDSL Advice:\n"
|
61
|
-
"- Look at the Model comments - often the model will provide a hint about what went wrong.\n"
|
62
|
-
"- If the model's response doesn't make sense, try rephrasing your question.\n"
|
63
|
-
"- Try using 'use_code' parameter of a MultipleChoice.\n"
|
64
|
-
"- A QuestionFreeText will almost always validate.\n"
|
65
|
-
"- Try setting the 'permissive' = True parameter in the Question constructor."
|
66
|
-
)
|
67
|
-
|
68
|
-
|
69
|
-
# Example usage:
|
70
|
-
if __name__ == "__main__":
|
71
|
-
try:
|
72
|
-
# Your validation code here
|
73
|
-
raise ValidationError.parse_obj({"answer": "120"})
|
74
|
-
except ValidationError as e:
|
75
|
-
explainer = ExceptionExplainer(e, "120")
|
76
|
-
explanation = explainer.explain()
|
77
|
-
print(explanation)
|