edsl 0.1.39.dev3__py3-none-any.whl → 0.1.39.dev4__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 +413 -332
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +57 -49
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +1071 -867
- edsl/agents/AgentList.py +551 -413
- edsl/agents/Invigilator.py +284 -233
- edsl/agents/InvigilatorBase.py +257 -270
- edsl/agents/PromptConstructor.py +272 -354
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +2 -3
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -117
- edsl/auto/StageBase.py +243 -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 +74 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +218 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +177 -157
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +59 -58
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1106 -1028
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +573 -555
- edsl/data/CacheEntry.py +230 -233
- edsl/data/CacheHandler.py +168 -149
- edsl/data/RemoteCacheSync.py +186 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +5 -4
- edsl/data/hack.py +10 -0
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +74 -73
- edsl/enums.py +202 -175
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +54 -42
- 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/inference_services.py +5 -0
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +109 -91
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +29 -22
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +106 -87
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -120
- edsl/inference_services/AzureAI.py +215 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +143 -148
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +80 -147
- edsl/inference_services/InferenceServicesCollection.py +138 -97
- edsl/inference_services/MistralAIService.py +120 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +236 -224
- edsl/inference_services/PerplexityService.py +160 -163
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -89
- edsl/inference_services/TogetherAIService.py +172 -170
- edsl/inference_services/data_structures.py +134 -0
- 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/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +43 -56
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +823 -898
- edsl/jobs/JobsChecks.py +172 -147
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -268
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -239
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +283 -251
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/check_survey_scenario_compatibility.py +85 -0
- edsl/jobs/data_structures.py +120 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +396 -661
- 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/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/results_exceptions_handler.py +98 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +151 -466
- edsl/jobs/runners/JobsRunnerStatus.py +297 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +470 -450
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +161 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +626 -668
- edsl/language_models/ModelList.py +164 -155
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +2 -3
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/model.py +256 -0
- edsl/language_models/repair.py +156 -156
- edsl/language_models/utilities.py +65 -64
- edsl/notebooks/Notebook.py +263 -258
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +352 -362
- edsl/prompts/__init__.py +2 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -664
- edsl/questions/QuestionBasePromptsMixin.py +221 -217
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +180 -182
- edsl/questions/QuestionFreeText.py +113 -114
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +223 -231
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +330 -286
- edsl/questions/QuestionNumerical.py +151 -153
- edsl/questions/QuestionRank.py +314 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/SimpleAskMixin.py +74 -73
- edsl/questions/__init__.py +27 -26
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +334 -289
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +90 -87
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +427 -413
- edsl/questions/loop_processor.py +149 -0
- 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/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +168 -161
- edsl/questions/question_registry.py +177 -177
- edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +71 -71
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +188 -174
- edsl/questions/response_validator_factory.py +34 -0
- 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/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- 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 +587 -424
- edsl/results/DatasetExportMixin.py +594 -731
- edsl/results/DatasetTree.py +295 -275
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +557 -465
- edsl/results/Results.py +1183 -1165
- edsl/results/ResultsExportMixin.py +45 -43
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/TableDisplay.py +125 -198
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +2 -2
- edsl/results/file_exports.py +252 -0
- edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +33 -33
- edsl/results/{Selector.py → results_selector.py} +145 -135
- edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +98 -98
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +77 -77
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +511 -632
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +498 -601
- edsl/scenarios/ScenarioHtmlMixin.py +65 -64
- edsl/scenarios/ScenarioList.py +1458 -1287
- edsl/scenarios/ScenarioListExportMixin.py +45 -52
- edsl/scenarios/ScenarioListPdfMixin.py +239 -261
- edsl/scenarios/__init__.py +3 -4
- edsl/scenarios/directory_scanner.py +96 -0
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +38 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/scenarios/{ScenarioJoin.py → scenario_join.py} +131 -127
- edsl/scenarios/scenario_selector.py +156 -0
- 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 +521 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +327 -326
- edsl/surveys/RuleCollection.py +385 -387
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1280 -1801
- edsl/surveys/SurveyCSS.py +273 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +181 -179
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +5 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +60 -56
- edsl/surveys/instructions/ChangeInstruction.py +48 -49
- edsl/surveys/instructions/Instruction.py +56 -65
- edsl/surveys/instructions/InstructionCollection.py +82 -77
- 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/test_h +1 -0
- 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/PrettyList.py +56 -0
- 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/gcp_bucket/example.py +50 -0
- edsl/utilities/interface.py +627 -627
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +436 -424
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/METADATA +13 -11
- edsl-0.1.39.dev4.dist-info/RECORD +361 -0
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/registry.py +0 -190
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.39.dev3.dist-info/RECORD +0 -277
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev4.dist-info}/WHEEL +0 -0
edsl/agents/Invigilator.py
CHANGED
@@ -1,233 +1,284 @@
|
|
1
|
-
"""Module for creating Invigilators, which are objects to administer a question to an Agent."""
|
2
|
-
|
3
|
-
from typing import Dict, Any, Optional
|
4
|
-
|
5
|
-
from edsl.
|
6
|
-
from edsl.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
from edsl.
|
12
|
-
from edsl.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
return self.
|
60
|
-
|
61
|
-
def _remove_from_cache(self, cache_key) -> None:
|
62
|
-
"""Remove an entry from the cache."""
|
63
|
-
if cache_key:
|
64
|
-
del self.cache.data[cache_key]
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
if self.
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
"
|
186
|
-
"
|
187
|
-
"
|
188
|
-
"
|
189
|
-
"
|
190
|
-
"
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
if
|
231
|
-
|
232
|
-
|
233
|
-
|
1
|
+
"""Module for creating Invigilators, which are objects to administer a question to an Agent."""
|
2
|
+
|
3
|
+
from typing import Dict, Any, Optional, TYPE_CHECKING
|
4
|
+
|
5
|
+
from edsl.utilities.decorators import sync_wrapper
|
6
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
7
|
+
from edsl.agents.InvigilatorBase import InvigilatorBase
|
8
|
+
from edsl.data_transfer_models import AgentResponseDict, EDSLResultObjectInput
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from edsl.prompts.Prompt import Prompt
|
12
|
+
from edsl.scenarios.Scenario import Scenario
|
13
|
+
from edsl.surveys.Survey import Survey
|
14
|
+
|
15
|
+
|
16
|
+
NA = "Not Applicable"
|
17
|
+
|
18
|
+
|
19
|
+
class InvigilatorAI(InvigilatorBase):
|
20
|
+
"""An invigilator that uses an AI model to answer questions."""
|
21
|
+
|
22
|
+
def get_prompts(self) -> Dict[str, "Prompt"]:
|
23
|
+
"""Return the prompts used."""
|
24
|
+
return self.prompt_constructor.get_prompts()
|
25
|
+
|
26
|
+
async def async_get_agent_response(self) -> AgentResponseDict:
|
27
|
+
prompts = self.get_prompts()
|
28
|
+
params = {
|
29
|
+
"user_prompt": prompts["user_prompt"].text,
|
30
|
+
"system_prompt": prompts["system_prompt"].text,
|
31
|
+
}
|
32
|
+
if "encoded_image" in prompts:
|
33
|
+
params["encoded_image"] = prompts["encoded_image"]
|
34
|
+
raise NotImplementedError("encoded_image not implemented")
|
35
|
+
|
36
|
+
if "files_list" in prompts:
|
37
|
+
params["files_list"] = prompts["files_list"]
|
38
|
+
|
39
|
+
params.update({"iteration": self.iteration, "cache": self.cache})
|
40
|
+
params.update({"invigilator": self})
|
41
|
+
|
42
|
+
if self.key_lookup:
|
43
|
+
self.model.set_key_lookup(self.key_lookup)
|
44
|
+
|
45
|
+
return await self.model.async_get_response(**params)
|
46
|
+
|
47
|
+
def store_response(self, agent_response_dict: AgentResponseDict) -> None:
|
48
|
+
"""Store the response in the invigilator, in case it is needed later because of validation failure."""
|
49
|
+
self.raw_model_response = agent_response_dict.model_outputs.response
|
50
|
+
self.generated_tokens = agent_response_dict.edsl_dict.generated_tokens
|
51
|
+
|
52
|
+
async def async_answer_question(self) -> AgentResponseDict:
|
53
|
+
"""Answer a question using the AI model.
|
54
|
+
|
55
|
+
>>> i = InvigilatorAI.example()
|
56
|
+
"""
|
57
|
+
agent_response_dict = await self.async_get_agent_response()
|
58
|
+
self.store_response(agent_response_dict)
|
59
|
+
return self._extract_edsl_result_entry_and_validate(agent_response_dict)
|
60
|
+
|
61
|
+
def _remove_from_cache(self, cache_key) -> None:
|
62
|
+
"""Remove an entry from the cache."""
|
63
|
+
if cache_key:
|
64
|
+
del self.cache.data[cache_key]
|
65
|
+
|
66
|
+
def _determine_answer(self, raw_answer: str) -> Any:
|
67
|
+
"""Determine the answer from the raw answer.
|
68
|
+
|
69
|
+
>>> i = InvigilatorAI.example()
|
70
|
+
>>> i._determine_answer("SPAM!")
|
71
|
+
'SPAM!'
|
72
|
+
|
73
|
+
>>> from edsl.questions import QuestionMultipleChoice
|
74
|
+
>>> q = QuestionMultipleChoice(question_text = "How are you?", question_name = "how_are_you", question_options = ["Good", "Bad"], use_code = True)
|
75
|
+
>>> i = InvigilatorAI.example(question = q)
|
76
|
+
>>> i._determine_answer("1")
|
77
|
+
'Bad'
|
78
|
+
>>> i._determine_answer("0")
|
79
|
+
'Good'
|
80
|
+
|
81
|
+
This shows how the answer can depend on scenario details
|
82
|
+
|
83
|
+
>>> from edsl import Scenario
|
84
|
+
>>> s = Scenario({'feeling_options':['Good', 'Bad']})
|
85
|
+
>>> q = QuestionMultipleChoice(question_text = "How are you?", question_name = "how_are_you", question_options = "{{ feeling_options }}", use_code = True)
|
86
|
+
>>> i = InvigilatorAI.example(question = q, scenario = s)
|
87
|
+
>>> i._determine_answer("1")
|
88
|
+
'Bad'
|
89
|
+
|
90
|
+
>>> from edsl import QuestionList, QuestionMultipleChoice, Survey
|
91
|
+
>>> q1 = QuestionList(question_name = "favs", question_text = "What are your top 3 colors?")
|
92
|
+
>>> q2 = QuestionMultipleChoice(question_text = "What is your favorite color?", question_name = "best", question_options = "{{ favs.answer }}", use_code = True)
|
93
|
+
>>> survey = Survey([q1, q2])
|
94
|
+
>>> i = InvigilatorAI.example(question = q2, scenario = s, survey = survey)
|
95
|
+
>>> i.current_answers = {"favs": ["Green", "Blue", "Red"]}
|
96
|
+
>>> i._determine_answer("2")
|
97
|
+
'Red'
|
98
|
+
"""
|
99
|
+
substitution_dict = self._prepare_substitution_dict(
|
100
|
+
self.survey, self.current_answers, self.scenario
|
101
|
+
)
|
102
|
+
return self.question._translate_answer_code_to_answer(
|
103
|
+
raw_answer, substitution_dict
|
104
|
+
)
|
105
|
+
|
106
|
+
@staticmethod
|
107
|
+
def _prepare_substitution_dict(
|
108
|
+
survey: "Survey", current_answers: dict, scenario: "Scenario"
|
109
|
+
) -> Dict[str, Any]:
|
110
|
+
"""Prepares a substitution dictionary for the question based on the survey, current answers, and scenario.
|
111
|
+
|
112
|
+
This is necessary beause sometimes the model's answer to a question could depend on details in
|
113
|
+
the prompt that were provided by the answer to a previous question or a scenario detail.
|
114
|
+
|
115
|
+
Note that the question object is getting the answer & a the comment appended to it, as the
|
116
|
+
jinja2 template might be referencing these values with a dot notation.
|
117
|
+
|
118
|
+
"""
|
119
|
+
question_dict = survey.duplicate().question_names_to_questions()
|
120
|
+
|
121
|
+
# iterates through the current answers and updates the question_dict (which is all questions)
|
122
|
+
for other_question, answer in current_answers.items():
|
123
|
+
if other_question in question_dict:
|
124
|
+
question_dict[other_question].answer = answer
|
125
|
+
else:
|
126
|
+
# it might be a comment
|
127
|
+
if (
|
128
|
+
new_question := other_question.split("_comment")[0]
|
129
|
+
) in question_dict:
|
130
|
+
question_dict[new_question].comment = answer
|
131
|
+
|
132
|
+
return {**question_dict, **scenario}
|
133
|
+
|
134
|
+
def _extract_edsl_result_entry_and_validate(
|
135
|
+
self, agent_response_dict: AgentResponseDict
|
136
|
+
) -> EDSLResultObjectInput:
|
137
|
+
"""Extract the EDSL result entry and validate it."""
|
138
|
+
edsl_dict = agent_response_dict.edsl_dict._asdict()
|
139
|
+
exception_occurred = None
|
140
|
+
validated = False
|
141
|
+
try:
|
142
|
+
# if the question has jinja parameters, it is easier to make a new question with the parameters
|
143
|
+
if self.question.parameters:
|
144
|
+
prior_answers_dict = self.prompt_constructor.prior_answers_dict()
|
145
|
+
|
146
|
+
# question options have be treated differently because of dynamic question
|
147
|
+
# this logic is all in the prompt constructor
|
148
|
+
if "question_options" in self.question.data:
|
149
|
+
new_question_options = self.prompt_constructor.get_question_options(
|
150
|
+
self.question.data
|
151
|
+
)
|
152
|
+
if new_question_options != self.question.data["question_options"]:
|
153
|
+
# I don't love this direct writing but it seems to work
|
154
|
+
self.question.question_options = new_question_options
|
155
|
+
|
156
|
+
question_with_validators = self.question.render(
|
157
|
+
self.scenario | prior_answers_dict
|
158
|
+
)
|
159
|
+
question_with_validators.use_code = self.question.use_code
|
160
|
+
else:
|
161
|
+
question_with_validators = self.question
|
162
|
+
|
163
|
+
validated_edsl_dict = question_with_validators._validate_answer(edsl_dict)
|
164
|
+
answer = self._determine_answer(validated_edsl_dict["answer"])
|
165
|
+
comment = validated_edsl_dict.get("comment", "")
|
166
|
+
validated = True
|
167
|
+
except QuestionAnswerValidationError as e:
|
168
|
+
answer = None
|
169
|
+
comment = "The response was not valid."
|
170
|
+
# if self.raise_validation_errors:
|
171
|
+
exception_occurred = e
|
172
|
+
except Exception as non_validation_error:
|
173
|
+
answer = None
|
174
|
+
comment = "Some other error occurred."
|
175
|
+
exception_occurred = non_validation_error
|
176
|
+
finally:
|
177
|
+
# even if validation failes, we still return the result
|
178
|
+
data = {
|
179
|
+
"answer": answer,
|
180
|
+
"comment": comment,
|
181
|
+
"generated_tokens": agent_response_dict.edsl_dict.generated_tokens,
|
182
|
+
"question_name": self.question.question_name,
|
183
|
+
"prompts": self.get_prompts(),
|
184
|
+
"cached_response": agent_response_dict.model_outputs.cached_response,
|
185
|
+
"raw_model_response": agent_response_dict.model_outputs.response,
|
186
|
+
"cache_used": agent_response_dict.model_outputs.cache_used,
|
187
|
+
"cache_key": agent_response_dict.model_outputs.cache_key,
|
188
|
+
"validated": validated,
|
189
|
+
"exception_occurred": exception_occurred,
|
190
|
+
"cost": agent_response_dict.model_outputs.cost,
|
191
|
+
}
|
192
|
+
result = EDSLResultObjectInput(**data)
|
193
|
+
return result
|
194
|
+
|
195
|
+
answer_question = sync_wrapper(async_answer_question)
|
196
|
+
|
197
|
+
|
198
|
+
class InvigilatorHuman(InvigilatorBase):
|
199
|
+
"""An invigilator for when a human is answering the question."""
|
200
|
+
|
201
|
+
validate_response: bool = False
|
202
|
+
translate_response: bool = False
|
203
|
+
|
204
|
+
async def async_answer_question(self, iteration: int = 0) -> AgentResponseDict:
|
205
|
+
"""Return the answer to the question."""
|
206
|
+
comment = "This is a real survey response from a human."
|
207
|
+
|
208
|
+
def __repr__(self):
|
209
|
+
return f"{self.literal}"
|
210
|
+
|
211
|
+
exception_occurred = None
|
212
|
+
validated = False
|
213
|
+
try:
|
214
|
+
answer = self.agent.answer_question_directly(self.question, self.scenario)
|
215
|
+
self.raw_model_response = answer
|
216
|
+
|
217
|
+
if self.validate_response:
|
218
|
+
_ = self.question._validate_answer({"answer": answer})
|
219
|
+
if self.translate_response:
|
220
|
+
answer = self.question._translate_answer_code_to_answer(
|
221
|
+
answer, self.scenario
|
222
|
+
)
|
223
|
+
validated = True
|
224
|
+
except QuestionAnswerValidationError as e:
|
225
|
+
answer = None
|
226
|
+
if self.raise_validation_errors:
|
227
|
+
exception_occurred = e
|
228
|
+
except Exception as e:
|
229
|
+
answer = None
|
230
|
+
if self.raise_validation_errors:
|
231
|
+
exception_occurred = e
|
232
|
+
finally:
|
233
|
+
data = {
|
234
|
+
"generated_tokens": NA, # NotApplicable(),
|
235
|
+
"question_name": self.question.question_name,
|
236
|
+
"prompts": self.get_prompts(),
|
237
|
+
"cached_response": NA,
|
238
|
+
"raw_model_response": NA,
|
239
|
+
"cache_used": NA,
|
240
|
+
"cache_key": NA,
|
241
|
+
"answer": answer,
|
242
|
+
"comment": comment,
|
243
|
+
"validated": validated,
|
244
|
+
"exception_occurred": exception_occurred,
|
245
|
+
}
|
246
|
+
return EDSLResultObjectInput(**data)
|
247
|
+
|
248
|
+
|
249
|
+
class InvigilatorFunctional(InvigilatorBase):
|
250
|
+
"""A Invigilator for when the question has a answer_question_directly function."""
|
251
|
+
|
252
|
+
async def async_answer_question(self, iteration: int = 0) -> AgentResponseDict:
|
253
|
+
"""Return the answer to the question."""
|
254
|
+
func = self.question.answer_question_directly
|
255
|
+
answer = func(scenario=self.scenario, agent_traits=self.agent.traits)
|
256
|
+
|
257
|
+
return EDSLResultObjectInput(
|
258
|
+
generated_tokens=str(answer),
|
259
|
+
question_name=self.question.question_name,
|
260
|
+
prompts=self.get_prompts(),
|
261
|
+
cached_response=NA,
|
262
|
+
raw_model_response=NA,
|
263
|
+
cache_used=NA,
|
264
|
+
cache_key=NA,
|
265
|
+
answer=answer["answer"],
|
266
|
+
comment="This is the result of a functional question.",
|
267
|
+
validated=True,
|
268
|
+
exception_occurred=None,
|
269
|
+
)
|
270
|
+
|
271
|
+
def get_prompts(self) -> Dict[str, "Prompt"]:
|
272
|
+
from edsl.prompts.Prompt import Prompt
|
273
|
+
|
274
|
+
"""Return the prompts used."""
|
275
|
+
return {
|
276
|
+
"user_prompt": Prompt("NA"),
|
277
|
+
"system_prompt": Prompt("NA"),
|
278
|
+
}
|
279
|
+
|
280
|
+
|
281
|
+
if __name__ == "__main__":
|
282
|
+
import doctest
|
283
|
+
|
284
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|