edsl 0.1.39.dev3__py3-none-any.whl → 0.1.39.dev5__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/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/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/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.dev5.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/METADATA +13 -11
- edsl-0.1.39.dev5.dist-info/RECORD +358 -0
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/WHEEL +1 -1
- 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
@@ -1,98 +1,98 @@
|
|
1
|
-
"""Compose two questions where the answer to q1 is used as an input to q2."""
|
2
|
-
|
3
|
-
from edsl.questions import QuestionFunctional
|
4
|
-
from edsl.questions.QuestionBase import QuestionBase
|
5
|
-
from edsl.scenarios.Scenario import Scenario
|
6
|
-
|
7
|
-
|
8
|
-
def compose_questions(
|
9
|
-
q1: QuestionBase, q2: QuestionBase, question_name: str = None
|
10
|
-
) -> QuestionFunctional:
|
11
|
-
"""
|
12
|
-
Compose two questions where the answer to q1 is used as an input to q2.
|
13
|
-
|
14
|
-
The resulting question is a question that can be used like other questions.
|
15
|
-
Note that the same result can also be achieved in other ways:
|
16
|
-
- Using the `add_targeted_memory(q2, q1)` method in Survey
|
17
|
-
- Using the __add__ method in Question
|
18
|
-
"""
|
19
|
-
if question_name is None:
|
20
|
-
question_name = f"{q1.question_name}_{q2.question_name}"
|
21
|
-
if q1.question_name not in q2.question_text:
|
22
|
-
raise ValueError(
|
23
|
-
f"q2 requires a field not present in q1's answer. "
|
24
|
-
f"q1: {q1.question_name}, q2: {q2.question_name}"
|
25
|
-
)
|
26
|
-
|
27
|
-
def combo(
|
28
|
-
scenario: Scenario, agent_traits: dict[str, str] = None
|
29
|
-
) -> QuestionFunctional:
|
30
|
-
"""Return the answer to the second question given the answer to the first question."""
|
31
|
-
# get the answer to the first question
|
32
|
-
from edsl.agents.Agent import Agent
|
33
|
-
|
34
|
-
first_answer = (
|
35
|
-
q1.by(scenario)
|
36
|
-
.by(Agent(traits=agent_traits))
|
37
|
-
.run()
|
38
|
-
.select(q1.question_name)[0]
|
39
|
-
)
|
40
|
-
# update the scenario with the answer to the first question
|
41
|
-
scenario.update({q1.question_name: first_answer})
|
42
|
-
# get the answer to the second question
|
43
|
-
second_answer = (
|
44
|
-
q2.by(scenario)
|
45
|
-
.by(Agent(traits=agent_traits))
|
46
|
-
.run()
|
47
|
-
.select(q2.question_name)[0]
|
48
|
-
)
|
49
|
-
return second_answer
|
50
|
-
|
51
|
-
return QuestionFunctional(
|
52
|
-
question_name=question_name, question_text="functional", func=combo
|
53
|
-
)
|
54
|
-
|
55
|
-
|
56
|
-
# UNCOMMENT BELOW TO SEE HOW THIS WORKS
|
57
|
-
|
58
|
-
# if __name__ == "__main__":
|
59
|
-
# from edsl.questions import QuestionFreeText, QuestionFunctional
|
60
|
-
# from edsl.questions.compose_questions import compose_questions
|
61
|
-
# from edsl.scenarios.Scenario import Scenario
|
62
|
-
|
63
|
-
# q1 = QuestionFreeText(
|
64
|
-
# question_text="What is the capital of {{country}}", question_name="capital"
|
65
|
-
# )
|
66
|
-
# q2 = QuestionFreeText(
|
67
|
-
# question_text="What is the population of {{capital}}",
|
68
|
-
# question_name="population",
|
69
|
-
# )
|
70
|
-
# q3 = compose_questions(q1, q2)
|
71
|
-
|
72
|
-
# jobs = q3.by(
|
73
|
-
# Scenario({"country": "France"}),
|
74
|
-
# Scenario({"country": "Germany"}),
|
75
|
-
# Scenario({"country": "Greece"}),
|
76
|
-
# )
|
77
|
-
|
78
|
-
# print("Without an agent persona")
|
79
|
-
# results1 = jobs.run()
|
80
|
-
|
81
|
-
# print("Adding an agent persona")
|
82
|
-
# results2 = jobs.by(
|
83
|
-
# Agent(traits={"name": "Bob, who always mentions his travel agency."})
|
84
|
-
# ).run()
|
85
|
-
# results2.select("capital_population").table()
|
86
|
-
|
87
|
-
# q1 = QuestionFreeText(
|
88
|
-
# question_text="What is the capital of {{country}}", question_name="capital"
|
89
|
-
# )
|
90
|
-
# q2 = QuestionFreeText(
|
91
|
-
# question_text="Is this {{population}} large?", question_name="population"
|
92
|
-
# )
|
93
|
-
# q3 = compose_questions(q1, q2)
|
94
|
-
# print("Should throw an exception:")
|
95
|
-
# try:
|
96
|
-
# q3.by(Scenario({"country": "France"})).run()
|
97
|
-
# except ValueError as e:
|
98
|
-
# print(e)
|
1
|
+
"""Compose two questions where the answer to q1 is used as an input to q2."""
|
2
|
+
|
3
|
+
from edsl.questions import QuestionFunctional
|
4
|
+
from edsl.questions.QuestionBase import QuestionBase
|
5
|
+
from edsl.scenarios.Scenario import Scenario
|
6
|
+
|
7
|
+
|
8
|
+
def compose_questions(
|
9
|
+
q1: QuestionBase, q2: QuestionBase, question_name: str = None
|
10
|
+
) -> QuestionFunctional:
|
11
|
+
"""
|
12
|
+
Compose two questions where the answer to q1 is used as an input to q2.
|
13
|
+
|
14
|
+
The resulting question is a question that can be used like other questions.
|
15
|
+
Note that the same result can also be achieved in other ways:
|
16
|
+
- Using the `add_targeted_memory(q2, q1)` method in Survey
|
17
|
+
- Using the __add__ method in Question
|
18
|
+
"""
|
19
|
+
if question_name is None:
|
20
|
+
question_name = f"{q1.question_name}_{q2.question_name}"
|
21
|
+
if q1.question_name not in q2.question_text:
|
22
|
+
raise ValueError(
|
23
|
+
f"q2 requires a field not present in q1's answer. "
|
24
|
+
f"q1: {q1.question_name}, q2: {q2.question_name}"
|
25
|
+
)
|
26
|
+
|
27
|
+
def combo(
|
28
|
+
scenario: Scenario, agent_traits: dict[str, str] = None
|
29
|
+
) -> QuestionFunctional:
|
30
|
+
"""Return the answer to the second question given the answer to the first question."""
|
31
|
+
# get the answer to the first question
|
32
|
+
from edsl.agents.Agent import Agent
|
33
|
+
|
34
|
+
first_answer = (
|
35
|
+
q1.by(scenario)
|
36
|
+
.by(Agent(traits=agent_traits))
|
37
|
+
.run()
|
38
|
+
.select(q1.question_name)[0]
|
39
|
+
)
|
40
|
+
# update the scenario with the answer to the first question
|
41
|
+
scenario.update({q1.question_name: first_answer})
|
42
|
+
# get the answer to the second question
|
43
|
+
second_answer = (
|
44
|
+
q2.by(scenario)
|
45
|
+
.by(Agent(traits=agent_traits))
|
46
|
+
.run()
|
47
|
+
.select(q2.question_name)[0]
|
48
|
+
)
|
49
|
+
return second_answer
|
50
|
+
|
51
|
+
return QuestionFunctional(
|
52
|
+
question_name=question_name, question_text="functional", func=combo
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
# UNCOMMENT BELOW TO SEE HOW THIS WORKS
|
57
|
+
|
58
|
+
# if __name__ == "__main__":
|
59
|
+
# from edsl.questions import QuestionFreeText, QuestionFunctional
|
60
|
+
# from edsl.questions.compose_questions import compose_questions
|
61
|
+
# from edsl.scenarios.Scenario import Scenario
|
62
|
+
|
63
|
+
# q1 = QuestionFreeText(
|
64
|
+
# question_text="What is the capital of {{country}}", question_name="capital"
|
65
|
+
# )
|
66
|
+
# q2 = QuestionFreeText(
|
67
|
+
# question_text="What is the population of {{capital}}",
|
68
|
+
# question_name="population",
|
69
|
+
# )
|
70
|
+
# q3 = compose_questions(q1, q2)
|
71
|
+
|
72
|
+
# jobs = q3.by(
|
73
|
+
# Scenario({"country": "France"}),
|
74
|
+
# Scenario({"country": "Germany"}),
|
75
|
+
# Scenario({"country": "Greece"}),
|
76
|
+
# )
|
77
|
+
|
78
|
+
# print("Without an agent persona")
|
79
|
+
# results1 = jobs.run()
|
80
|
+
|
81
|
+
# print("Adding an agent persona")
|
82
|
+
# results2 = jobs.by(
|
83
|
+
# Agent(traits={"name": "Bob, who always mentions his travel agency."})
|
84
|
+
# ).run()
|
85
|
+
# results2.select("capital_population").table()
|
86
|
+
|
87
|
+
# q1 = QuestionFreeText(
|
88
|
+
# question_text="What is the capital of {{country}}", question_name="capital"
|
89
|
+
# )
|
90
|
+
# q2 = QuestionFreeText(
|
91
|
+
# question_text="Is this {{population}} large?", question_name="population"
|
92
|
+
# )
|
93
|
+
# q3 = compose_questions(q1, q2)
|
94
|
+
# print("Should throw an exception:")
|
95
|
+
# try:
|
96
|
+
# q3.by(Scenario({"country": "France"})).run()
|
97
|
+
# except ValueError as e:
|
98
|
+
# print(e)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from typing import Any, Optional, TypedDict
|
2
|
+
from pydantic import BaseModel
|
3
|
+
|
4
|
+
|
5
|
+
class RawEdslAnswerDict(TypedDict):
|
6
|
+
answer: Any
|
7
|
+
comment: Optional[str]
|
8
|
+
generated_tokens: Optional[str]
|
9
|
+
|
10
|
+
|
11
|
+
class BaseResponse(BaseModel):
|
12
|
+
answer: Any
|
13
|
+
comment: Optional[str] = None
|
14
|
+
generated_tokens: Optional[str] = None
|
15
|
+
|
16
|
+
|
17
|
+
class EdslAnswerDict(TypedDict):
|
18
|
+
answer: Any
|
19
|
+
comment: Optional[str]
|
20
|
+
generated_tokens: Optional[str]
|
edsl/questions/decorators.py
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
from typing import Optional, Callable, TypeVar
|
2
|
-
|
3
|
-
T = TypeVar("T")
|
4
|
-
|
5
|
-
|
6
|
-
def inject_exception(func: Callable[..., T]) -> Callable[..., T]:
|
7
|
-
def wrapper(
|
8
|
-
cls,
|
9
|
-
exception_to_throw: Optional[Exception] = None,
|
10
|
-
override_answer: Optional[dict] = None,
|
11
|
-
*args,
|
12
|
-
**kwargs
|
13
|
-
) -> T:
|
14
|
-
base_instance = func(cls, *args, **kwargs)
|
15
|
-
if exception_to_throw:
|
16
|
-
base_instance.exception_to_throw = exception_to_throw
|
17
|
-
if override_answer:
|
18
|
-
base_instance.override_answer = override_answer
|
19
|
-
return base_instance
|
20
|
-
|
21
|
-
return wrapper
|
1
|
+
from typing import Optional, Callable, TypeVar
|
2
|
+
|
3
|
+
T = TypeVar("T")
|
4
|
+
|
5
|
+
|
6
|
+
def inject_exception(func: Callable[..., T]) -> Callable[..., T]:
|
7
|
+
def wrapper(
|
8
|
+
cls,
|
9
|
+
exception_to_throw: Optional[Exception] = None,
|
10
|
+
override_answer: Optional[dict] = None,
|
11
|
+
*args,
|
12
|
+
**kwargs
|
13
|
+
) -> T:
|
14
|
+
base_instance = func(cls, *args, **kwargs)
|
15
|
+
if exception_to_throw:
|
16
|
+
base_instance.exception_to_throw = exception_to_throw
|
17
|
+
if override_answer:
|
18
|
+
base_instance.override_answer = override_answer
|
19
|
+
return base_instance
|
20
|
+
|
21
|
+
return wrapper
|
@@ -1,76 +1,76 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from typing import Optional
|
3
|
-
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
4
|
-
|
5
|
-
from edsl.questions.decorators import inject_exception
|
6
|
-
|
7
|
-
|
8
|
-
class QuestionLikertFive(QuestionMultipleChoice):
|
9
|
-
"""This question prompts the agent to respond to a statement on a 5-point Likert scale."""
|
10
|
-
|
11
|
-
question_type = "likert_five"
|
12
|
-
likert_options: list[str] = [
|
13
|
-
"Strongly disagree",
|
14
|
-
"Disagree",
|
15
|
-
"Neutral",
|
16
|
-
"Agree",
|
17
|
-
"Strongly agree",
|
18
|
-
]
|
19
|
-
|
20
|
-
def __init__(
|
21
|
-
self,
|
22
|
-
question_name: str,
|
23
|
-
question_text: str,
|
24
|
-
question_options: Optional[list[str]] = likert_options,
|
25
|
-
answering_instructions: Optional[str] = None,
|
26
|
-
question_presentation: Optional[str] = None,
|
27
|
-
include_comment: bool = True,
|
28
|
-
):
|
29
|
-
"""Initialize the question.
|
30
|
-
|
31
|
-
:param question_name: The name of the question.
|
32
|
-
:param question_text: The text of the question.
|
33
|
-
:param question_options: The options the respondent should select from (list of strings). If not provided, the default Likert options are used (['Strongly disagree', 'Disagree', 'Neutral', 'Agree', 'Strongly agree']). To view them, run `QuestionLikertFive.likert_options`.
|
34
|
-
"""
|
35
|
-
super().__init__(
|
36
|
-
question_name=question_name,
|
37
|
-
question_text=question_text,
|
38
|
-
question_options=question_options,
|
39
|
-
use_code=False,
|
40
|
-
include_comment=include_comment,
|
41
|
-
answering_instructions=answering_instructions,
|
42
|
-
question_presentation=question_presentation,
|
43
|
-
)
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
@inject_exception
|
47
|
-
def example(cls) -> QuestionLikertFive:
|
48
|
-
"""Return an example question."""
|
49
|
-
return cls(
|
50
|
-
question_name="happy_raining",
|
51
|
-
question_text="I'm only happy when it rains.",
|
52
|
-
)
|
53
|
-
|
54
|
-
|
55
|
-
def main():
|
56
|
-
"""Test QuestionLikertFive."""
|
57
|
-
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
58
|
-
|
59
|
-
q = QuestionLikertFive.example()
|
60
|
-
q.question_text
|
61
|
-
q.question_options
|
62
|
-
q.question_name
|
63
|
-
# validate an answer
|
64
|
-
q._validate_answer({"answer": 0, "comment": "I like custard"})
|
65
|
-
# translate answer code
|
66
|
-
q._translate_answer_code_to_answer(0, {})
|
67
|
-
q._simulate_answer()
|
68
|
-
q._simulate_answer(human_readable=False)
|
69
|
-
q._validate_answer(q._simulate_answer(human_readable=False))
|
70
|
-
# serialization (inherits from Question)
|
71
|
-
q.to_dict()
|
72
|
-
assert q.from_dict(q.to_dict()) == q
|
73
|
-
|
74
|
-
import doctest
|
75
|
-
|
76
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional
|
3
|
+
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
4
|
+
|
5
|
+
from edsl.questions.decorators import inject_exception
|
6
|
+
|
7
|
+
|
8
|
+
class QuestionLikertFive(QuestionMultipleChoice):
|
9
|
+
"""This question prompts the agent to respond to a statement on a 5-point Likert scale."""
|
10
|
+
|
11
|
+
question_type = "likert_five"
|
12
|
+
likert_options: list[str] = [
|
13
|
+
"Strongly disagree",
|
14
|
+
"Disagree",
|
15
|
+
"Neutral",
|
16
|
+
"Agree",
|
17
|
+
"Strongly agree",
|
18
|
+
]
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
question_name: str,
|
23
|
+
question_text: str,
|
24
|
+
question_options: Optional[list[str]] = likert_options,
|
25
|
+
answering_instructions: Optional[str] = None,
|
26
|
+
question_presentation: Optional[str] = None,
|
27
|
+
include_comment: bool = True,
|
28
|
+
):
|
29
|
+
"""Initialize the question.
|
30
|
+
|
31
|
+
:param question_name: The name of the question.
|
32
|
+
:param question_text: The text of the question.
|
33
|
+
:param question_options: The options the respondent should select from (list of strings). If not provided, the default Likert options are used (['Strongly disagree', 'Disagree', 'Neutral', 'Agree', 'Strongly agree']). To view them, run `QuestionLikertFive.likert_options`.
|
34
|
+
"""
|
35
|
+
super().__init__(
|
36
|
+
question_name=question_name,
|
37
|
+
question_text=question_text,
|
38
|
+
question_options=question_options,
|
39
|
+
use_code=False,
|
40
|
+
include_comment=include_comment,
|
41
|
+
answering_instructions=answering_instructions,
|
42
|
+
question_presentation=question_presentation,
|
43
|
+
)
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
@inject_exception
|
47
|
+
def example(cls) -> QuestionLikertFive:
|
48
|
+
"""Return an example question."""
|
49
|
+
return cls(
|
50
|
+
question_name="happy_raining",
|
51
|
+
question_text="I'm only happy when it rains.",
|
52
|
+
)
|
53
|
+
|
54
|
+
|
55
|
+
def main():
|
56
|
+
"""Test QuestionLikertFive."""
|
57
|
+
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
58
|
+
|
59
|
+
q = QuestionLikertFive.example()
|
60
|
+
q.question_text
|
61
|
+
q.question_options
|
62
|
+
q.question_name
|
63
|
+
# validate an answer
|
64
|
+
q._validate_answer({"answer": 0, "comment": "I like custard"})
|
65
|
+
# translate answer code
|
66
|
+
q._translate_answer_code_to_answer(0, {})
|
67
|
+
q._simulate_answer()
|
68
|
+
q._simulate_answer(human_readable=False)
|
69
|
+
q._validate_answer(q._simulate_answer(human_readable=False))
|
70
|
+
# serialization (inherits from Question)
|
71
|
+
q.to_dict()
|
72
|
+
assert q.from_dict(q.to_dict()) == q
|
73
|
+
|
74
|
+
import doctest
|
75
|
+
|
76
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -1,87 +1,90 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from typing import Optional
|
3
|
-
|
4
|
-
from edsl.questions.descriptors import QuestionOptionsDescriptor, OptionLabelDescriptor
|
5
|
-
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
6
|
-
|
7
|
-
from edsl.questions.decorators import inject_exception
|
8
|
-
|
9
|
-
|
10
|
-
class QuestionLinearScale(QuestionMultipleChoice):
|
11
|
-
"""This question prompts the agent to respond to a statement on a linear scale."""
|
12
|
-
|
13
|
-
question_type = "linear_scale"
|
14
|
-
option_labels: Optional[dict[int, str]] = OptionLabelDescriptor()
|
15
|
-
question_options = QuestionOptionsDescriptor(linear_scale=True)
|
16
|
-
|
17
|
-
def __init__(
|
18
|
-
self,
|
19
|
-
question_name: str,
|
20
|
-
question_text: str,
|
21
|
-
question_options: list[int],
|
22
|
-
option_labels: Optional[dict[int, str]] = None,
|
23
|
-
answering_instructions: Optional[str] = None,
|
24
|
-
question_presentation: Optional[str] = None,
|
25
|
-
include_comment: Optional[bool] = True,
|
26
|
-
):
|
27
|
-
"""Instantiate a new QuestionLinearScale.
|
28
|
-
|
29
|
-
:param question_name: The name of the question.
|
30
|
-
:param question_text: The text of the question.
|
31
|
-
:param question_options: The options the respondent should select from.
|
32
|
-
:param option_labels: Maps question_options to labels.
|
33
|
-
:param instructions: Instructions for the question. If not provided, the default instructions are used. To view them, run `QuestionLinearScale.default_instructions`.
|
34
|
-
"""
|
35
|
-
super().__init__(
|
36
|
-
question_name=question_name,
|
37
|
-
question_text=question_text,
|
38
|
-
question_options=question_options,
|
39
|
-
use_code=False, # question linear scale will have it's own code
|
40
|
-
include_comment=include_comment,
|
41
|
-
)
|
42
|
-
self.question_options = question_options
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
q.
|
73
|
-
|
74
|
-
q.
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
q.
|
80
|
-
|
81
|
-
|
82
|
-
q.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from edsl.questions.descriptors import QuestionOptionsDescriptor, OptionLabelDescriptor
|
5
|
+
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
6
|
+
|
7
|
+
from edsl.questions.decorators import inject_exception
|
8
|
+
|
9
|
+
|
10
|
+
class QuestionLinearScale(QuestionMultipleChoice):
|
11
|
+
"""This question prompts the agent to respond to a statement on a linear scale."""
|
12
|
+
|
13
|
+
question_type = "linear_scale"
|
14
|
+
option_labels: Optional[dict[int, str]] = OptionLabelDescriptor()
|
15
|
+
question_options = QuestionOptionsDescriptor(linear_scale=True)
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
question_name: str,
|
20
|
+
question_text: str,
|
21
|
+
question_options: list[int],
|
22
|
+
option_labels: Optional[dict[int, str]] = None,
|
23
|
+
answering_instructions: Optional[str] = None,
|
24
|
+
question_presentation: Optional[str] = None,
|
25
|
+
include_comment: Optional[bool] = True,
|
26
|
+
):
|
27
|
+
"""Instantiate a new QuestionLinearScale.
|
28
|
+
|
29
|
+
:param question_name: The name of the question.
|
30
|
+
:param question_text: The text of the question.
|
31
|
+
:param question_options: The options the respondent should select from.
|
32
|
+
:param option_labels: Maps question_options to labels.
|
33
|
+
:param instructions: Instructions for the question. If not provided, the default instructions are used. To view them, run `QuestionLinearScale.default_instructions`.
|
34
|
+
"""
|
35
|
+
super().__init__(
|
36
|
+
question_name=question_name,
|
37
|
+
question_text=question_text,
|
38
|
+
question_options=question_options,
|
39
|
+
use_code=False, # question linear scale will have it's own code
|
40
|
+
include_comment=include_comment,
|
41
|
+
)
|
42
|
+
self.question_options = question_options
|
43
|
+
if isinstance(option_labels, str):
|
44
|
+
self.option_labels = option_labels
|
45
|
+
else:
|
46
|
+
self.option_labels = (
|
47
|
+
{int(k): v for k, v in option_labels.items()} if option_labels else {}
|
48
|
+
)
|
49
|
+
self.answering_instructions = answering_instructions
|
50
|
+
self.question_presentation = question_presentation
|
51
|
+
|
52
|
+
################
|
53
|
+
# Helpful
|
54
|
+
################
|
55
|
+
@classmethod
|
56
|
+
@inject_exception
|
57
|
+
def example(cls, include_comment: bool = True) -> QuestionLinearScale:
|
58
|
+
"""Return an example of a linear scale question."""
|
59
|
+
return cls(
|
60
|
+
question_text="How much do you like ice cream?",
|
61
|
+
question_options=[1, 2, 3, 4, 5],
|
62
|
+
question_name="ice_cream",
|
63
|
+
option_labels={1: "I hate it", 5: "I love it"},
|
64
|
+
include_comment=include_comment,
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
def main():
|
69
|
+
"""Create an example of a linear scale question and demonstrate its functionality."""
|
70
|
+
from edsl.questions.derived.QuestionLinearScale import QuestionLinearScale
|
71
|
+
|
72
|
+
q = QuestionLinearScale.example()
|
73
|
+
q.question_text
|
74
|
+
q.question_options
|
75
|
+
q.question_name
|
76
|
+
# validate an answer
|
77
|
+
q._validate_answer({"answer": 3, "comment": "I like custard"})
|
78
|
+
# translate answer code
|
79
|
+
q._translate_answer_code_to_answer(3, {})
|
80
|
+
# simulate answer
|
81
|
+
q._simulate_answer()
|
82
|
+
q._simulate_answer(human_readable=False)
|
83
|
+
q._validate_answer(q._simulate_answer(human_readable=False))
|
84
|
+
# serialization (inherits from Question)
|
85
|
+
q.to_dict()
|
86
|
+
assert q.from_dict(q.to_dict()) == q
|
87
|
+
|
88
|
+
import doctest
|
89
|
+
|
90
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|