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
@@ -0,0 +1,128 @@
|
|
1
|
+
from typing import Dict, List, Set
|
2
|
+
from warnings import warn
|
3
|
+
from edsl.prompts.Prompt import Prompt
|
4
|
+
|
5
|
+
from edsl.agents.QuestionTemplateReplacementsBuilder import (
|
6
|
+
QuestionTemplateReplacementsBuilder as QTRB,
|
7
|
+
)
|
8
|
+
|
9
|
+
|
10
|
+
class QuestionInstructionPromptBuilder:
|
11
|
+
"""Handles the construction and rendering of question instructions."""
|
12
|
+
|
13
|
+
def __init__(self, prompt_constructor: "PromptConstructor"):
|
14
|
+
self.prompt_constructor = prompt_constructor
|
15
|
+
|
16
|
+
self.model = self.prompt_constructor.model
|
17
|
+
self.survey = self.prompt_constructor.survey
|
18
|
+
self.question = self.prompt_constructor.question
|
19
|
+
|
20
|
+
def build(self) -> Prompt:
|
21
|
+
"""Builds the complete question instructions prompt with all necessary components.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
Prompt: The fully rendered question instructions
|
25
|
+
"""
|
26
|
+
base_prompt = self._create_base_prompt()
|
27
|
+
enriched_prompt = self._enrich_with_question_options(base_prompt)
|
28
|
+
rendered_prompt = self._render_prompt(enriched_prompt)
|
29
|
+
self._validate_template_variables(rendered_prompt)
|
30
|
+
|
31
|
+
return self._append_survey_instructions(rendered_prompt)
|
32
|
+
|
33
|
+
def _create_base_prompt(self) -> Dict:
|
34
|
+
"""Creates the initial prompt with basic question data.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
Dict: Base question data
|
38
|
+
"""
|
39
|
+
return {
|
40
|
+
"prompt": Prompt(self.question.get_instructions(model=self.model.model)),
|
41
|
+
"data": self.question.data.copy(),
|
42
|
+
}
|
43
|
+
|
44
|
+
def _enrich_with_question_options(self, prompt_data: Dict) -> Dict:
|
45
|
+
"""Enriches the prompt data with question options if they exist.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
prompt_data: Dictionary containing prompt and question data
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
Dict: Enriched prompt data
|
52
|
+
"""
|
53
|
+
if "question_options" in prompt_data["data"]:
|
54
|
+
from edsl.agents.question_option_processor import QuestionOptionProcessor
|
55
|
+
|
56
|
+
question_options = QuestionOptionProcessor(
|
57
|
+
self.prompt_constructor
|
58
|
+
).get_question_options(question_data=prompt_data["data"])
|
59
|
+
|
60
|
+
prompt_data["data"]["question_options"] = question_options
|
61
|
+
return prompt_data
|
62
|
+
|
63
|
+
def _render_prompt(self, prompt_data: Dict) -> Prompt:
|
64
|
+
"""Renders the prompt using the replacement dictionary.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
prompt_data: Dictionary containing prompt and question data
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Prompt: Rendered instructions
|
71
|
+
"""
|
72
|
+
|
73
|
+
replacement_dict = QTRB(self.prompt_constructor).build_replacement_dict(
|
74
|
+
prompt_data["data"]
|
75
|
+
)
|
76
|
+
return prompt_data["prompt"].render(replacement_dict)
|
77
|
+
|
78
|
+
def _validate_template_variables(self, rendered_prompt: Prompt) -> None:
|
79
|
+
"""Validates that all template variables have been properly replaced.
|
80
|
+
|
81
|
+
Args:
|
82
|
+
rendered_prompt: The rendered prompt to validate
|
83
|
+
|
84
|
+
Warns:
|
85
|
+
If any template variables remain undefined
|
86
|
+
"""
|
87
|
+
undefined_vars = rendered_prompt.undefined_template_variables({})
|
88
|
+
|
89
|
+
# Check for question names in undefined variables
|
90
|
+
self._check_question_names_in_undefined_vars(undefined_vars)
|
91
|
+
|
92
|
+
# Warn about any remaining undefined variables
|
93
|
+
if undefined_vars:
|
94
|
+
warn(f"Question instructions still has variables: {undefined_vars}.")
|
95
|
+
|
96
|
+
def _check_question_names_in_undefined_vars(self, undefined_vars: Set[str]) -> None:
|
97
|
+
"""Checks if any undefined variables match question names in the survey.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
undefined_vars: Set of undefined template variables
|
101
|
+
"""
|
102
|
+
for question_name in self.survey.question_names:
|
103
|
+
if question_name in undefined_vars:
|
104
|
+
print(
|
105
|
+
f"Question name found in undefined_template_variables: {question_name}"
|
106
|
+
)
|
107
|
+
|
108
|
+
def _append_survey_instructions(self, rendered_prompt: Prompt) -> Prompt:
|
109
|
+
"""Appends any relevant survey instructions to the rendered prompt.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
rendered_prompt: The rendered prompt to append instructions to
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
Prompt: Final prompt with survey instructions
|
116
|
+
"""
|
117
|
+
relevant_instructions = self.survey._relevant_instructions(
|
118
|
+
self.question.question_name
|
119
|
+
)
|
120
|
+
|
121
|
+
if not relevant_instructions:
|
122
|
+
return rendered_prompt
|
123
|
+
|
124
|
+
preamble = Prompt(text="")
|
125
|
+
for instruction in relevant_instructions:
|
126
|
+
preamble += instruction.text
|
127
|
+
|
128
|
+
return preamble + rendered_prompt
|
@@ -0,0 +1,137 @@
|
|
1
|
+
from jinja2 import Environment, meta
|
2
|
+
from typing import Any, Set, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from edsl.agents.PromptConstructor import PromptConstructor
|
6
|
+
from edsl.scenarios.Scenario import Scenario
|
7
|
+
|
8
|
+
|
9
|
+
class QuestionTemplateReplacementsBuilder:
|
10
|
+
def __init__(self, prompt_constructor: "PromptConstructor"):
|
11
|
+
self.prompt_constructor = prompt_constructor
|
12
|
+
|
13
|
+
def question_file_keys(self):
|
14
|
+
question_text = self.prompt_constructor.question.question_text
|
15
|
+
file_keys = self._find_file_keys(self.prompt_constructor.scenario)
|
16
|
+
return self._extract_file_keys_from_question_text(question_text, file_keys)
|
17
|
+
|
18
|
+
def scenario_file_keys(self):
|
19
|
+
return self._find_file_keys(self.prompt_constructor.scenario)
|
20
|
+
|
21
|
+
def get_jinja2_variables(template_str: str) -> Set[str]:
|
22
|
+
"""
|
23
|
+
Extracts all variable names from a Jinja2 template using Jinja2's built-in parsing.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
template_str (str): The Jinja2 template string
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
Set[str]: A set of variable names found in the template
|
30
|
+
"""
|
31
|
+
env = Environment()
|
32
|
+
ast = env.parse(template_str)
|
33
|
+
return meta.find_undeclared_variables(ast)
|
34
|
+
|
35
|
+
@staticmethod
|
36
|
+
def _find_file_keys(scenario: "Scenario") -> list:
|
37
|
+
"""We need to find all the keys in the scenario that refer to FileStore objects.
|
38
|
+
These will be used to append to the prompt a list of files that are part of the scenario.
|
39
|
+
|
40
|
+
>>> from edsl import Scenario
|
41
|
+
>>> from edsl.scenarios.FileStore import FileStore
|
42
|
+
>>> import tempfile
|
43
|
+
>>> with tempfile.NamedTemporaryFile() as f:
|
44
|
+
... _ = f.write(b"Hello, world!")
|
45
|
+
... _ = f.seek(0)
|
46
|
+
... fs = FileStore(f.name)
|
47
|
+
... scenario = Scenario({"fs_file": fs, 'a': 1})
|
48
|
+
... QuestionTemplateReplacementsBuilder._find_file_keys(scenario)
|
49
|
+
['fs_file']
|
50
|
+
"""
|
51
|
+
from edsl.scenarios.FileStore import FileStore
|
52
|
+
|
53
|
+
file_entries = []
|
54
|
+
for key, value in scenario.items():
|
55
|
+
if isinstance(value, FileStore):
|
56
|
+
file_entries.append(key)
|
57
|
+
return file_entries
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def _extract_file_keys_from_question_text(
|
61
|
+
question_text: str, scenario_file_keys: list
|
62
|
+
) -> list:
|
63
|
+
"""
|
64
|
+
Extracts the file keys from a question text.
|
65
|
+
|
66
|
+
>>> from edsl import Scenario
|
67
|
+
>>> from edsl.scenarios.FileStore import FileStore
|
68
|
+
>>> import tempfile
|
69
|
+
>>> with tempfile.NamedTemporaryFile() as f:
|
70
|
+
... _ = f.write(b"Hello, world!")
|
71
|
+
... _ = f.seek(0)
|
72
|
+
... fs = FileStore(f.name)
|
73
|
+
... scenario = Scenario({"fs_file": fs, 'a': 1})
|
74
|
+
... QuestionTemplateReplacementsBuilder._extract_file_keys_from_question_text("{{ fs_file }}", ['fs_file'])
|
75
|
+
['fs_file']
|
76
|
+
"""
|
77
|
+
variables = QuestionTemplateReplacementsBuilder.get_jinja2_variables(
|
78
|
+
question_text
|
79
|
+
)
|
80
|
+
question_file_keys = []
|
81
|
+
for var in variables:
|
82
|
+
if var in scenario_file_keys:
|
83
|
+
question_file_keys.append(var)
|
84
|
+
return question_file_keys
|
85
|
+
|
86
|
+
def _scenario_replacements(self) -> dict[str, Any]:
|
87
|
+
# File references dictionary
|
88
|
+
file_refs = {key: f"<see file {key}>" for key in self.scenario_file_keys()}
|
89
|
+
|
90
|
+
# Scenario items excluding file keys
|
91
|
+
scenario_items = {
|
92
|
+
k: v
|
93
|
+
for k, v in self.prompt_constructor.scenario.items()
|
94
|
+
if k not in self.scenario_file_keys()
|
95
|
+
}
|
96
|
+
return {**file_refs, **scenario_items}
|
97
|
+
|
98
|
+
@staticmethod
|
99
|
+
def _question_data_replacements(
|
100
|
+
question: dict, question_data: dict
|
101
|
+
) -> dict[str, Any]:
|
102
|
+
"""Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources.
|
103
|
+
|
104
|
+
>>> from edsl import QuestionMultipleChoice
|
105
|
+
>>> q = QuestionMultipleChoice(question_text="Do you like school?", question_name = "q0", question_options = ["yes", "no"])
|
106
|
+
>>> QuestionTemplateReplacementsBuilder._question_data_replacements(q, q.data)
|
107
|
+
{'use_code': False, 'include_comment': True, 'question_name': 'q0', 'question_text': 'Do you like school?', 'question_options': ['yes', 'no']}
|
108
|
+
|
109
|
+
"""
|
110
|
+
question_settings = {
|
111
|
+
"use_code": getattr(question, "_use_code", True),
|
112
|
+
"include_comment": getattr(question, "_include_comment", False),
|
113
|
+
}
|
114
|
+
return {**question_settings, **question_data}
|
115
|
+
|
116
|
+
def build_replacement_dict(self, question_data: dict) -> dict[str, Any]:
|
117
|
+
"""Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources."""
|
118
|
+
rpl = {}
|
119
|
+
rpl["scenario"] = self._scenario_replacements()
|
120
|
+
rpl["question"] = self._question_data_replacements(
|
121
|
+
self.prompt_constructor.question, question_data
|
122
|
+
)
|
123
|
+
rpl["prior_answers"] = self.prompt_constructor.prior_answers_dict()
|
124
|
+
rpl["agent"] = {"agent": self.prompt_constructor.agent}
|
125
|
+
|
126
|
+
# Combine all dictionaries using dict.update() for clarity
|
127
|
+
replacement_dict = {}
|
128
|
+
for r in rpl.values():
|
129
|
+
replacement_dict.update(r)
|
130
|
+
|
131
|
+
return replacement_dict
|
132
|
+
|
133
|
+
|
134
|
+
if __name__ == "__main__":
|
135
|
+
import doctest
|
136
|
+
|
137
|
+
doctest.testmod()
|
edsl/agents/__init__.py
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
from edsl.agents.Agent import Agent
|
2
|
-
from edsl.agents.AgentList import AgentList
|
3
|
-
from edsl.agents.InvigilatorBase import InvigilatorBase
|
1
|
+
from edsl.agents.Agent import Agent
|
2
|
+
from edsl.agents.AgentList import AgentList
|
edsl/agents/descriptors.py
CHANGED
@@ -1,99 +1,99 @@
|
|
1
|
-
"""This module contains the descriptors used to set the attributes of the Agent class."""
|
2
|
-
|
3
|
-
from typing import Dict
|
4
|
-
from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
|
5
|
-
|
6
|
-
|
7
|
-
def convert_agent_name(x):
|
8
|
-
# potentially a numpy int64
|
9
|
-
import numpy as np
|
10
|
-
|
11
|
-
if isinstance(x, np.int64):
|
12
|
-
return int(x)
|
13
|
-
elif x is None:
|
14
|
-
return None
|
15
|
-
elif isinstance(x, int):
|
16
|
-
return x
|
17
|
-
else:
|
18
|
-
return str(x)
|
19
|
-
|
20
|
-
|
21
|
-
class NameDescriptor:
|
22
|
-
"""ABC for something."""
|
23
|
-
|
24
|
-
def __get__(self, instance, owner):
|
25
|
-
"""Return the value of the attribute."""
|
26
|
-
return instance.__dict__[self.name]
|
27
|
-
|
28
|
-
def __set__(self, instance, name: str) -> None:
|
29
|
-
"""Set the value of the attribute."""
|
30
|
-
instance.__dict__[self.name] = convert_agent_name(name)
|
31
|
-
|
32
|
-
def __set_name__(self, owner, name: str) -> None:
|
33
|
-
"""Set the name of the attribute."""
|
34
|
-
self.name = "_" + name
|
35
|
-
|
36
|
-
|
37
|
-
class TraitsDescriptor:
|
38
|
-
"""Traits descriptor."""
|
39
|
-
|
40
|
-
def __get__(self, instance, owner):
|
41
|
-
"""Return the value of the attribute."""
|
42
|
-
return instance.__dict__[self.name]
|
43
|
-
|
44
|
-
def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
|
45
|
-
"""Set the value of the attribute."""
|
46
|
-
from edsl.utilities.utilities import is_valid_variable_name
|
47
|
-
|
48
|
-
for key, value in traits_dict.items():
|
49
|
-
if key == "name":
|
50
|
-
raise AgentNameError(
|
51
|
-
"Trait keys cannot be 'name'. Instead, use the 'name' attribute directly e.g.,\n"
|
52
|
-
'Agent(name="my_agent", traits={"trait1": "value1", "trait2": "value2"})'
|
53
|
-
)
|
54
|
-
|
55
|
-
if not is_valid_variable_name(key):
|
56
|
-
raise AgentTraitKeyError(
|
57
|
-
f"""Trait keys must be valid Python identifiers (must be alphanumeric, cannot start with a number and must use underscores instead of spaces).
|
58
|
-
You passed: {key}
|
59
|
-
"""
|
60
|
-
)
|
61
|
-
|
62
|
-
instance.__dict__[self.name] = traits_dict
|
63
|
-
|
64
|
-
def __set_name__(self, owner, name: str) -> None:
|
65
|
-
"""Set the name of the attribute."""
|
66
|
-
self.name = name
|
67
|
-
|
68
|
-
|
69
|
-
class CodebookDescriptor:
|
70
|
-
"""ABC for something."""
|
71
|
-
|
72
|
-
def __get__(self, instance, owner):
|
73
|
-
"""Return the value of the attribute."""
|
74
|
-
return instance.__dict__[self.name]
|
75
|
-
|
76
|
-
def __set__(self, instance, codebook_dict: Dict[str, str]) -> None:
|
77
|
-
"""Set the value of the attribute."""
|
78
|
-
instance.__dict__[self.name] = codebook_dict
|
79
|
-
|
80
|
-
def __set_name__(self, owner, name: str) -> None:
|
81
|
-
"""Set the name of the attribute."""
|
82
|
-
self.name = "_" + name
|
83
|
-
|
84
|
-
|
85
|
-
class InstructionDescriptor:
|
86
|
-
"""ABC for something."""
|
87
|
-
|
88
|
-
def __get__(self, instance, owner):
|
89
|
-
"""Return the value of the attribute."""
|
90
|
-
return instance.__dict__[self.name]
|
91
|
-
|
92
|
-
def __set__(self, instance, instruction) -> None:
|
93
|
-
"""Set the value of the attribute."""
|
94
|
-
instance.__dict__[self.name] = instruction
|
95
|
-
instance.set_instructions = instruction != instance.default_instruction
|
96
|
-
|
97
|
-
def __set_name__(self, owner, name: str) -> None:
|
98
|
-
"""Set the name of the attribute."""
|
99
|
-
self.name = "_" + name
|
1
|
+
"""This module contains the descriptors used to set the attributes of the Agent class."""
|
2
|
+
|
3
|
+
from typing import Dict
|
4
|
+
from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
|
5
|
+
|
6
|
+
|
7
|
+
def convert_agent_name(x):
|
8
|
+
# potentially a numpy int64
|
9
|
+
import numpy as np
|
10
|
+
|
11
|
+
if isinstance(x, np.int64):
|
12
|
+
return int(x)
|
13
|
+
elif x is None:
|
14
|
+
return None
|
15
|
+
elif isinstance(x, int):
|
16
|
+
return x
|
17
|
+
else:
|
18
|
+
return str(x)
|
19
|
+
|
20
|
+
|
21
|
+
class NameDescriptor:
|
22
|
+
"""ABC for something."""
|
23
|
+
|
24
|
+
def __get__(self, instance, owner):
|
25
|
+
"""Return the value of the attribute."""
|
26
|
+
return instance.__dict__[self.name]
|
27
|
+
|
28
|
+
def __set__(self, instance, name: str) -> None:
|
29
|
+
"""Set the value of the attribute."""
|
30
|
+
instance.__dict__[self.name] = convert_agent_name(name)
|
31
|
+
|
32
|
+
def __set_name__(self, owner, name: str) -> None:
|
33
|
+
"""Set the name of the attribute."""
|
34
|
+
self.name = "_" + name
|
35
|
+
|
36
|
+
|
37
|
+
class TraitsDescriptor:
|
38
|
+
"""Traits descriptor."""
|
39
|
+
|
40
|
+
def __get__(self, instance, owner):
|
41
|
+
"""Return the value of the attribute."""
|
42
|
+
return instance.__dict__[self.name]
|
43
|
+
|
44
|
+
def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
|
45
|
+
"""Set the value of the attribute."""
|
46
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
47
|
+
|
48
|
+
for key, value in traits_dict.items():
|
49
|
+
if key == "name":
|
50
|
+
raise AgentNameError(
|
51
|
+
"Trait keys cannot be 'name'. Instead, use the 'name' attribute directly e.g.,\n"
|
52
|
+
'Agent(name="my_agent", traits={"trait1": "value1", "trait2": "value2"})'
|
53
|
+
)
|
54
|
+
|
55
|
+
if not is_valid_variable_name(key):
|
56
|
+
raise AgentTraitKeyError(
|
57
|
+
f"""Trait keys must be valid Python identifiers (must be alphanumeric, cannot start with a number and must use underscores instead of spaces).
|
58
|
+
You passed: {key}
|
59
|
+
"""
|
60
|
+
)
|
61
|
+
|
62
|
+
instance.__dict__[self.name] = traits_dict
|
63
|
+
|
64
|
+
def __set_name__(self, owner, name: str) -> None:
|
65
|
+
"""Set the name of the attribute."""
|
66
|
+
self.name = name
|
67
|
+
|
68
|
+
|
69
|
+
class CodebookDescriptor:
|
70
|
+
"""ABC for something."""
|
71
|
+
|
72
|
+
def __get__(self, instance, owner):
|
73
|
+
"""Return the value of the attribute."""
|
74
|
+
return instance.__dict__[self.name]
|
75
|
+
|
76
|
+
def __set__(self, instance, codebook_dict: Dict[str, str]) -> None:
|
77
|
+
"""Set the value of the attribute."""
|
78
|
+
instance.__dict__[self.name] = codebook_dict
|
79
|
+
|
80
|
+
def __set_name__(self, owner, name: str) -> None:
|
81
|
+
"""Set the name of the attribute."""
|
82
|
+
self.name = "_" + name
|
83
|
+
|
84
|
+
|
85
|
+
class InstructionDescriptor:
|
86
|
+
"""ABC for something."""
|
87
|
+
|
88
|
+
def __get__(self, instance, owner):
|
89
|
+
"""Return the value of the attribute."""
|
90
|
+
return instance.__dict__[self.name]
|
91
|
+
|
92
|
+
def __set__(self, instance, instruction) -> None:
|
93
|
+
"""Set the value of the attribute."""
|
94
|
+
instance.__dict__[self.name] = instruction
|
95
|
+
instance.set_instructions = instruction != instance.default_instruction
|
96
|
+
|
97
|
+
def __set_name__(self, owner, name: str) -> None:
|
98
|
+
"""Set the name of the attribute."""
|
99
|
+
self.name = "_" + name
|