edsl 0.1.14__py3-none-any.whl → 0.1.40__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 +348 -38
- edsl/BaseDiff.py +260 -0
- edsl/TemplateLoader.py +24 -0
- edsl/__init__.py +46 -10
- edsl/__version__.py +1 -0
- edsl/agents/Agent.py +842 -144
- edsl/agents/AgentList.py +521 -25
- edsl/agents/Invigilator.py +250 -374
- edsl/agents/InvigilatorBase.py +257 -0
- edsl/agents/PromptConstructor.py +272 -0
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/descriptors.py +43 -13
- edsl/agents/prompt_helpers.py +129 -0
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -0
- edsl/auto/StageBase.py +243 -0
- edsl/auto/StageGenerateSurvey.py +178 -0
- edsl/auto/StageLabelQuestions.py +125 -0
- edsl/auto/StagePersona.py +61 -0
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
- edsl/auto/StagePersonaDimensionValues.py +74 -0
- edsl/auto/StagePersonaDimensions.py +69 -0
- edsl/auto/StageQuestions.py +74 -0
- edsl/auto/SurveyCreatorPipeline.py +21 -0
- edsl/auto/utilities.py +218 -0
- edsl/base/Base.py +279 -0
- edsl/config.py +121 -104
- edsl/conversation/Conversation.py +290 -0
- edsl/conversation/car_buying.py +59 -0
- edsl/conversation/chips.py +95 -0
- edsl/conversation/mug_negotiation.py +81 -0
- edsl/conversation/next_speaker_utilities.py +93 -0
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -0
- edsl/coop/__init__.py +1 -0
- edsl/coop/coop.py +1029 -134
- edsl/coop/utils.py +131 -0
- edsl/data/Cache.py +560 -89
- edsl/data/CacheEntry.py +230 -0
- edsl/data/CacheHandler.py +168 -0
- edsl/data/RemoteCacheSync.py +186 -0
- edsl/data/SQLiteDict.py +292 -0
- edsl/data/__init__.py +5 -3
- edsl/data/orm.py +6 -33
- edsl/data_transfer_models.py +74 -27
- edsl/enums.py +165 -8
- edsl/exceptions/BaseException.py +21 -0
- edsl/exceptions/__init__.py +52 -46
- edsl/exceptions/agents.py +33 -15
- edsl/exceptions/cache.py +5 -0
- edsl/exceptions/coop.py +8 -0
- edsl/exceptions/general.py +34 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/jobs.py +15 -0
- edsl/exceptions/language_models.py +46 -1
- edsl/exceptions/questions.py +80 -5
- edsl/exceptions/results.py +16 -5
- edsl/exceptions/scenarios.py +29 -0
- edsl/exceptions/surveys.py +13 -10
- edsl/inference_services/AnthropicService.py +106 -0
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -0
- edsl/inference_services/AzureAI.py +215 -0
- edsl/inference_services/DeepInfraService.py +18 -0
- edsl/inference_services/GoogleService.py +143 -0
- edsl/inference_services/GroqService.py +20 -0
- edsl/inference_services/InferenceServiceABC.py +80 -0
- edsl/inference_services/InferenceServicesCollection.py +138 -0
- edsl/inference_services/MistralAIService.py +120 -0
- edsl/inference_services/OllamaService.py +18 -0
- edsl/inference_services/OpenAIService.py +236 -0
- edsl/inference_services/PerplexityService.py +160 -0
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -0
- edsl/inference_services/TogetherAIService.py +172 -0
- edsl/inference_services/data_structures.py +134 -0
- edsl/inference_services/models_available_cache.py +118 -0
- edsl/inference_services/rate_limits_cache.py +25 -0
- edsl/inference_services/registry.py +41 -0
- edsl/inference_services/write_available.py +10 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +21 -20
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +684 -204
- edsl/jobs/JobsChecks.py +172 -0
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -0
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -0
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -0
- edsl/jobs/buckets/ModelBuckets.py +65 -0
- edsl/jobs/buckets/TokenBucket.py +283 -0
- 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 +392 -0
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -0
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -0
- edsl/jobs/interviews/InterviewStatistic.py +63 -0
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -0
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -0
- edsl/jobs/interviews/InterviewStatusLog.py +92 -0
- edsl/jobs/interviews/ReportErrors.py +66 -0
- edsl/jobs/interviews/interview_status_enum.py +9 -0
- 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 -110
- edsl/jobs/runners/JobsRunnerStatus.py +298 -0
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -0
- edsl/jobs/tasks/TaskCreators.py +64 -0
- edsl/jobs/tasks/TaskHistory.py +470 -0
- edsl/jobs/tasks/TaskStatusLog.py +23 -0
- edsl/jobs/tasks/task_status_enum.py +161 -0
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -0
- edsl/jobs/tokens/TokenUsage.py +34 -0
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +507 -386
- edsl/language_models/ModelList.py +164 -0
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -0
- edsl/language_models/__init__.py +1 -8
- edsl/language_models/fake_openai_call.py +15 -0
- edsl/language_models/fake_openai_service.py +61 -0
- 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 +109 -41
- edsl/language_models/utilities.py +65 -0
- edsl/notebooks/Notebook.py +263 -0
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -0
- edsl/prompts/Prompt.py +222 -93
- edsl/prompts/__init__.py +1 -1
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -0
- edsl/questions/QuestionBasePromptsMixin.py +221 -0
- edsl/questions/QuestionBudget.py +164 -67
- edsl/questions/QuestionCheckBox.py +281 -62
- edsl/questions/QuestionDict.py +343 -0
- edsl/questions/QuestionExtract.py +136 -50
- edsl/questions/QuestionFreeText.py +79 -55
- edsl/questions/QuestionFunctional.py +138 -41
- edsl/questions/QuestionList.py +184 -57
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +293 -69
- edsl/questions/QuestionNumerical.py +109 -56
- edsl/questions/QuestionRank.py +244 -49
- edsl/questions/Quick.py +41 -0
- edsl/questions/SimpleAskMixin.py +74 -0
- edsl/questions/__init__.py +9 -6
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +153 -38
- edsl/questions/compose_questions.py +13 -7
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -0
- edsl/questions/derived/QuestionLikertFive.py +28 -26
- edsl/questions/derived/QuestionLinearScale.py +41 -28
- edsl/questions/derived/QuestionTopK.py +34 -26
- edsl/questions/derived/QuestionYesNo.py +40 -27
- edsl/questions/descriptors.py +228 -74
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/prompt_templates/question_budget.jinja +13 -0
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
- edsl/questions/prompt_templates/question_extract.jinja +11 -0
- edsl/questions/prompt_templates/question_free_text.jinja +3 -0
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
- edsl/questions/prompt_templates/question_list.jinja +17 -0
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
- edsl/questions/prompt_templates/question_numerical.jinja +37 -0
- edsl/questions/question_base_gen_mixin.py +168 -0
- edsl/questions/question_registry.py +130 -46
- edsl/questions/register_questions_meta.py +71 -0
- edsl/questions/response_validator_abc.py +188 -0
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/settings.py +5 -2
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +7 -0
- edsl/questions/templates/budget/question_presentation.jinja +7 -0
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
- edsl/questions/templates/dict/__init__.py +0 -0
- edsl/questions/templates/dict/answering_instructions.jinja +21 -0
- edsl/questions/templates/dict/question_presentation.jinja +1 -0
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/extract/answering_instructions.jinja +7 -0
- edsl/questions/templates/extract/question_presentation.jinja +1 -0
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +1 -0
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
- edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +4 -0
- edsl/questions/templates/list/question_presentation.jinja +5 -0
- 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/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +7 -0
- edsl/questions/templates/numerical/question_presentation.jinja +7 -0
- edsl/questions/templates/rank/__init__.py +0 -0
- edsl/questions/templates/rank/answering_instructions.jinja +11 -0
- edsl/questions/templates/rank/question_presentation.jinja +15 -0
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
- edsl/questions/templates/top_k/question_presentation.jinja +22 -0
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
- edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
- edsl/results/CSSParameterizer.py +108 -0
- edsl/results/Dataset.py +550 -19
- edsl/results/DatasetExportMixin.py +594 -0
- edsl/results/DatasetTree.py +295 -0
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +477 -173
- edsl/results/Results.py +987 -269
- edsl/results/ResultsExportMixin.py +28 -125
- edsl/results/ResultsGGMixin.py +83 -15
- edsl/results/TableDisplay.py +125 -0
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/file_exports.py +252 -0
- edsl/results/results_fetch_mixin.py +33 -0
- edsl/results/results_selector.py +145 -0
- edsl/results/results_tools_mixin.py +98 -0
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +78 -0
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +543 -0
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +431 -62
- edsl/scenarios/ScenarioHtmlMixin.py +65 -0
- edsl/scenarios/ScenarioList.py +1415 -45
- edsl/scenarios/ScenarioListExportMixin.py +45 -0
- edsl/scenarios/ScenarioListPdfMixin.py +239 -0
- edsl/scenarios/__init__.py +2 -0
- 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 +49 -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/scenario_join.py +131 -0
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/shared.py +1 -0
- edsl/study/ObjectEntry.py +173 -0
- edsl/study/ProofOfWork.py +113 -0
- edsl/study/SnapShot.py +80 -0
- edsl/study/Study.py +521 -0
- edsl/study/__init__.py +4 -0
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +92 -11
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +9 -4
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +156 -35
- edsl/surveys/Rule.py +221 -74
- edsl/surveys/RuleCollection.py +241 -61
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1079 -339
- edsl/surveys/SurveyCSS.py +273 -0
- edsl/surveys/SurveyExportMixin.py +235 -40
- edsl/surveys/SurveyFlowVisualization.py +181 -0
- edsl/surveys/SurveyQualtricsImport.py +284 -0
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/base.py +19 -3
- edsl/surveys/descriptors.py +17 -6
- edsl/surveys/instructions/ChangeInstruction.py +48 -0
- edsl/surveys/instructions/Instruction.py +56 -0
- edsl/surveys/instructions/InstructionCollection.py +82 -0
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +24 -0
- edsl/templates/error_reporting/exceptions_by_model.html +35 -0
- edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
- edsl/templates/error_reporting/exceptions_by_type.html +17 -0
- edsl/templates/error_reporting/interview_details.html +116 -0
- edsl/templates/error_reporting/interviews.html +19 -0
- edsl/templates/error_reporting/overview.html +5 -0
- edsl/templates/error_reporting/performance_plot.html +2 -0
- edsl/templates/error_reporting/report.css +74 -0
- edsl/templates/error_reporting/report.html +118 -0
- edsl/templates/error_reporting/report.js +25 -0
- edsl/tools/__init__.py +1 -0
- edsl/tools/clusters.py +192 -0
- edsl/tools/embeddings.py +27 -0
- edsl/tools/embeddings_plotting.py +118 -0
- edsl/tools/plotting.py +112 -0
- edsl/tools/summarize.py +18 -0
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/SystemInfo.py +5 -0
- edsl/utilities/__init__.py +21 -20
- edsl/utilities/ast_utilities.py +3 -0
- edsl/utilities/data/Registry.py +2 -0
- edsl/utilities/decorators.py +41 -0
- edsl/utilities/gcp_bucket/__init__.py +0 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
- edsl/utilities/interface.py +310 -60
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -0
- edsl/utilities/restricted_python.py +70 -0
- edsl/utilities/utilities.py +203 -13
- edsl-0.1.40.dist-info/METADATA +111 -0
- edsl-0.1.40.dist-info/RECORD +362 -0
- {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/WHEEL +1 -1
- edsl/agents/AgentListExportMixin.py +0 -24
- edsl/coop/old.py +0 -31
- edsl/data/Database.py +0 -141
- edsl/data/crud.py +0 -121
- edsl/jobs/Interview.py +0 -417
- edsl/jobs/JobsRunner.py +0 -63
- edsl/jobs/JobsRunnerStatusMixin.py +0 -115
- edsl/jobs/base.py +0 -47
- edsl/jobs/buckets.py +0 -166
- edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
- edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
- edsl/jobs/task_management.py +0 -218
- edsl/jobs/token_tracking.py +0 -78
- edsl/language_models/DeepInfra.py +0 -69
- edsl/language_models/OpenAI.py +0 -98
- edsl/language_models/model_interfaces/GeminiPro.py +0 -66
- edsl/language_models/model_interfaces/LanguageModelOpenAIFour.py +0 -8
- edsl/language_models/model_interfaces/LanguageModelOpenAIThreeFiveTurbo.py +0 -8
- edsl/language_models/model_interfaces/LlamaTwo13B.py +0 -21
- edsl/language_models/model_interfaces/LlamaTwo70B.py +0 -21
- edsl/language_models/model_interfaces/Mixtral8x7B.py +0 -24
- edsl/language_models/registry.py +0 -81
- edsl/language_models/schemas.py +0 -15
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/prompts/QuestionInstructionsBase.py +0 -6
- edsl/prompts/library/agent_instructions.py +0 -29
- edsl/prompts/library/agent_persona.py +0 -17
- edsl/prompts/library/question_budget.py +0 -26
- edsl/prompts/library/question_checkbox.py +0 -32
- edsl/prompts/library/question_extract.py +0 -19
- edsl/prompts/library/question_freetext.py +0 -14
- edsl/prompts/library/question_linear_scale.py +0 -20
- edsl/prompts/library/question_list.py +0 -22
- edsl/prompts/library/question_multiple_choice.py +0 -44
- edsl/prompts/library/question_numerical.py +0 -31
- edsl/prompts/library/question_rank.py +0 -21
- edsl/prompts/prompt_config.py +0 -33
- edsl/prompts/registry.py +0 -185
- edsl/questions/Question.py +0 -240
- edsl/report/InputOutputDataTypes.py +0 -134
- edsl/report/RegressionMixin.py +0 -28
- edsl/report/ReportOutputs.py +0 -1228
- edsl/report/ResultsFetchMixin.py +0 -106
- edsl/report/ResultsOutputMixin.py +0 -14
- edsl/report/demo.ipynb +0 -645
- edsl/results/ResultsDBMixin.py +0 -184
- edsl/surveys/SurveyFlowVisualizationMixin.py +0 -92
- edsl/trackers/Tracker.py +0 -91
- edsl/trackers/TrackerAPI.py +0 -196
- edsl/trackers/TrackerTasks.py +0 -70
- edsl/utilities/pastebin.py +0 -141
- edsl-0.1.14.dist-info/METADATA +0 -69
- edsl-0.1.14.dist-info/RECORD +0 -141
- /edsl/{language_models/model_interfaces → inference_services}/__init__.py +0 -0
- /edsl/{report/__init__.py → jobs/runners/JobsRunnerStatusData.py} +0 -0
- /edsl/{trackers/__init__.py → language_models/ServiceDataSources.py} +0 -0
- {edsl-0.1.14.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
import math
|
2
|
+
|
3
|
+
|
4
|
+
def logprob_to_prob(logprob):
|
5
|
+
if logprob is None:
|
6
|
+
return None
|
7
|
+
return math.exp(logprob)
|
8
|
+
|
9
|
+
|
10
|
+
def format_output(data):
|
11
|
+
from rich.table import Table
|
12
|
+
from rich.console import Console
|
13
|
+
|
14
|
+
content = data["choices"][0]["logprobs"]["content"]
|
15
|
+
table = Table(show_header=True, header_style="bold magenta")
|
16
|
+
|
17
|
+
# First pass to determine the maximum number of top tokens
|
18
|
+
max_tokens = max(len(item["top_logprobs"]) for item in content)
|
19
|
+
|
20
|
+
# Set up the table columns
|
21
|
+
table.add_column("Token", style="bold")
|
22
|
+
for i in range(max_tokens):
|
23
|
+
table.add_column(f"Top Token {i+1}")
|
24
|
+
|
25
|
+
for item in content:
|
26
|
+
token = item["token"].strip()
|
27
|
+
top_tokens = [
|
28
|
+
(top["token"].strip(), logprob_to_prob(top.get("logprob")))
|
29
|
+
for top in item["top_logprobs"]
|
30
|
+
]
|
31
|
+
|
32
|
+
# Row data starts with the main token
|
33
|
+
row = [token]
|
34
|
+
|
35
|
+
# Add each top token with coloring based on probability
|
36
|
+
for token, probability in top_tokens:
|
37
|
+
if probability is not None:
|
38
|
+
# Assign color based on probability value
|
39
|
+
if probability > 0.5:
|
40
|
+
color = "green"
|
41
|
+
elif probability > 0.2:
|
42
|
+
color = "yellow"
|
43
|
+
else:
|
44
|
+
color = "red"
|
45
|
+
formatted_token = f"[{color}]{token} ({probability:.2f})[/]"
|
46
|
+
else:
|
47
|
+
formatted_token = f"{token} (N/A)"
|
48
|
+
row.append(formatted_token)
|
49
|
+
|
50
|
+
# Ensure row has enough columns
|
51
|
+
while len(row) < max_tokens + 1:
|
52
|
+
row.append("")
|
53
|
+
|
54
|
+
table.add_row(*row)
|
55
|
+
|
56
|
+
console = Console()
|
57
|
+
console.print(table)
|
58
|
+
|
59
|
+
|
60
|
+
class SimpleAskMixin:
|
61
|
+
# def simple_ask(self, question: QuestionBase, system_prompt = "You are a helpful agent pretending to be a human.", top_logprobs = 2):
|
62
|
+
|
63
|
+
def simple_ask(
|
64
|
+
self,
|
65
|
+
model=None,
|
66
|
+
system_prompt="You are a helpful agent pretending to be a human. Do not break character",
|
67
|
+
top_logprobs=4,
|
68
|
+
):
|
69
|
+
from edsl.language_models.model import Model
|
70
|
+
|
71
|
+
if model is None:
|
72
|
+
model = Model()
|
73
|
+
response = model.simple_ask(self, system_prompt, top_logprobs)
|
74
|
+
return format_output(response)
|
edsl/questions/__init__.py
CHANGED
@@ -1,25 +1,28 @@
|
|
1
1
|
# Schemas
|
2
2
|
from edsl.questions.settings import Settings
|
3
|
+
from edsl.questions.register_questions_meta import RegisterQuestionsMeta
|
3
4
|
|
4
5
|
# Base Class
|
5
|
-
from edsl.questions.
|
6
|
+
from edsl.questions.QuestionBase import QuestionBase
|
6
7
|
|
7
8
|
# Core Questions
|
8
|
-
from edsl.questions.QuestionBudget import QuestionBudget
|
9
9
|
from edsl.questions.QuestionCheckBox import QuestionCheckBox
|
10
10
|
from edsl.questions.QuestionExtract import QuestionExtract
|
11
11
|
from edsl.questions.QuestionFreeText import QuestionFreeText
|
12
12
|
from edsl.questions.QuestionFunctional import QuestionFunctional
|
13
13
|
from edsl.questions.QuestionList import QuestionList
|
14
|
+
from edsl.questions.QuestionMatrix import QuestionMatrix
|
15
|
+
from edsl.questions.QuestionDict import QuestionDict
|
14
16
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
15
17
|
from edsl.questions.QuestionNumerical import QuestionNumerical
|
18
|
+
from edsl.questions.QuestionBudget import QuestionBudget
|
16
19
|
from edsl.questions.QuestionRank import QuestionRank
|
17
20
|
|
18
|
-
# Questions derived from core questions
|
21
|
+
# # # Questions derived from core questions
|
19
22
|
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
20
23
|
from edsl.questions.derived.QuestionLinearScale import QuestionLinearScale
|
21
|
-
from edsl.questions.derived.QuestionTopK import QuestionTopK
|
22
24
|
from edsl.questions.derived.QuestionYesNo import QuestionYesNo
|
25
|
+
from edsl.questions.derived.QuestionTopK import QuestionTopK
|
23
26
|
|
24
|
-
# Compose Questions
|
25
|
-
from edsl.questions.compose_questions import compose_questions
|
27
|
+
# # Compose Questions
|
28
|
+
# from edsl.questions.compose_questions import compose_questions
|
@@ -1,24 +1,42 @@
|
|
1
|
+
"""Mixin with validators for LLM answers to questions."""
|
2
|
+
|
1
3
|
import re
|
2
4
|
from typing import Any, Type, Union
|
3
|
-
from edsl.exceptions import (
|
5
|
+
from edsl.exceptions.questions import (
|
4
6
|
QuestionAnswerValidationError,
|
5
7
|
)
|
6
8
|
|
7
9
|
|
8
10
|
class AnswerValidatorMixin:
|
9
11
|
"""
|
10
|
-
Mixin with validators for LLM answers to questions
|
12
|
+
Mixin with validators for LLM answers to questions.
|
13
|
+
|
11
14
|
- Template validation: validators for the entire answer object format
|
12
15
|
- Value validation: validators for specific values
|
13
16
|
- Question specific validation: validators for specific question types
|
14
17
|
"""
|
15
18
|
|
19
|
+
def failing_job(self):
|
20
|
+
from edsl import Agent
|
21
|
+
|
22
|
+
a = Agent()
|
23
|
+
|
24
|
+
def f(self, question, scenario):
|
25
|
+
return []
|
26
|
+
|
27
|
+
a.add_direct_question_answering_method(f, validate_response=True)
|
28
|
+
from edsl import QuestionNumerical
|
29
|
+
|
30
|
+
q = QuestionNumerical.example()
|
31
|
+
results = q.by(a).run()
|
32
|
+
return results
|
33
|
+
|
16
34
|
#####################
|
17
35
|
# TEMPLATE VALIDATION
|
18
36
|
#####################
|
19
|
-
def
|
20
|
-
"""
|
21
|
-
|
37
|
+
def _validate_answer_template_basic(self, answer: Any) -> None:
|
38
|
+
"""Check that the answer (i) is a dictionary (ii) has an 'answer' key.
|
39
|
+
|
22
40
|
- E.g., both {'answer': 1} and {'answer': {'a': 1}, 'other_key'=[1,2,3]} are valid
|
23
41
|
"""
|
24
42
|
if not isinstance(answer, dict):
|
@@ -33,21 +51,25 @@ class AnswerValidatorMixin:
|
|
33
51
|
#####################
|
34
52
|
# VALUE VALIDATION
|
35
53
|
#####################
|
36
|
-
def
|
54
|
+
def _validate_answer_key_value(
|
37
55
|
self, answer: dict[str, Any], key: str, of_type: Type
|
38
56
|
) -> None:
|
39
|
-
"""
|
57
|
+
"""Check that the value of a key is of the specified type."""
|
40
58
|
if not isinstance(answer.get(key), of_type):
|
41
59
|
raise QuestionAnswerValidationError(
|
42
60
|
f"""Answer key '{key}' must be of type {of_type.__name__};
|
43
61
|
(got {answer.get(key)}) which is of type {type(answer.get(key))}."""
|
44
62
|
)
|
45
63
|
|
46
|
-
def
|
64
|
+
def _validate_answer_key_value_numeric(
|
47
65
|
self, answer: dict[str, Any], key: str
|
48
66
|
) -> None:
|
49
|
-
"""
|
67
|
+
"""Check that the value is numeric (int or float).
|
68
|
+
Can also deal with strings that contain commas and other characters.
|
69
|
+
|
70
|
+
"""
|
50
71
|
value = answer.get(key)
|
72
|
+
initial_value = value
|
51
73
|
if type(value) == str:
|
52
74
|
value = value.replace(",", "")
|
53
75
|
value = "".join(re.findall(r"[-+]?\d*\.\d+|\d+", value))
|
@@ -59,7 +81,7 @@ class AnswerValidatorMixin:
|
|
59
81
|
value = float(value)
|
60
82
|
except ValueError:
|
61
83
|
raise QuestionAnswerValidationError(
|
62
|
-
f"Answer should be numerical (int or float)."
|
84
|
+
f"Answer should be numerical (int or float). Got '{initial_value}'"
|
63
85
|
)
|
64
86
|
return None
|
65
87
|
elif type(value) == int or type(value) == float:
|
@@ -72,9 +94,10 @@ class AnswerValidatorMixin:
|
|
72
94
|
#####################
|
73
95
|
# QUESTION SPECIFIC VALIDATION
|
74
96
|
#####################
|
75
|
-
def
|
76
|
-
"""
|
77
|
-
|
97
|
+
def _validate_answer_budget(self, answer: dict[str, Any]) -> None:
|
98
|
+
"""Validate QuestionBudget-specific answer.
|
99
|
+
|
100
|
+
Check that answer["answer"]:
|
78
101
|
- has keys that are in the range of the number of options
|
79
102
|
- has values that are non-negative integers
|
80
103
|
- has values that sum to `budget_sum`
|
@@ -95,17 +118,21 @@ class AnswerValidatorMixin:
|
|
95
118
|
)
|
96
119
|
if any([int(key) not in acceptable_answer_keys for key in answer.keys()]):
|
97
120
|
raise QuestionAnswerValidationError(
|
98
|
-
f"Budget keys must be in {acceptable_answer_keys}, but got {answer_keys}"
|
121
|
+
f"Budget keys must be in {acceptable_answer_keys}, but got {answer_keys}."
|
99
122
|
)
|
100
123
|
if acceptable_answer_keys != answer_keys:
|
101
124
|
missing_keys = acceptable_answer_keys - answer_keys
|
102
125
|
raise QuestionAnswerValidationError(
|
103
|
-
f"All but keys must be represented in the answer. Missing: {missing_keys}"
|
126
|
+
f"All but keys must be represented in the answer. Missing: {missing_keys}."
|
104
127
|
)
|
105
128
|
|
106
|
-
def
|
107
|
-
"""
|
108
|
-
|
129
|
+
def _validate_answer_checkbox(self, answer: dict[str, Union[str, int]]) -> None:
|
130
|
+
"""Validate QuestionCheckbox-specific answer.
|
131
|
+
|
132
|
+
:param answer: Answer to validate
|
133
|
+
|
134
|
+
|
135
|
+
Check that answer["answer"]:
|
109
136
|
- has elements that are strings, bytes-like objects or real numbers evaluating to integers
|
110
137
|
- has elements that are in the range of the number of options
|
111
138
|
- has at least `min_selections` elements, if provided
|
@@ -126,16 +153,17 @@ class AnswerValidatorMixin:
|
|
126
153
|
)
|
127
154
|
if self.min_selections is not None and len(answer_codes) < self.min_selections:
|
128
155
|
raise QuestionAnswerValidationError(
|
129
|
-
f"Answer {answer_codes} has fewer than {self.min_selections} options selected."
|
156
|
+
f"Answer codes, {answer_codes}, has fewer than {self.min_selections} options selected."
|
130
157
|
)
|
131
158
|
if self.max_selections is not None and len(answer_codes) > self.max_selections:
|
132
159
|
raise QuestionAnswerValidationError(
|
133
|
-
f"Answer {answer_codes} has more than {self.max_selections} options selected."
|
160
|
+
f"Answer codes, {answer_codes}, has more than {self.max_selections} options selected."
|
134
161
|
)
|
135
162
|
|
136
|
-
def
|
137
|
-
"""
|
138
|
-
|
163
|
+
def _validate_answer_extract(self, answer: dict[str, Any]) -> None:
|
164
|
+
"""Validate QuestionExtract-specific answer.
|
165
|
+
|
166
|
+
Check that answer["answer"]:
|
139
167
|
- does not have keys that are not in the answer template
|
140
168
|
- has all keys that are in the answer template
|
141
169
|
"""
|
@@ -143,16 +171,17 @@ class AnswerValidatorMixin:
|
|
143
171
|
acceptable_answer_keys = set(self.answer_template.keys())
|
144
172
|
if any([key not in acceptable_answer_keys for key in value.keys()]):
|
145
173
|
raise QuestionAnswerValidationError(
|
146
|
-
f"Answer keys must be in {acceptable_answer_keys}, but got {value.keys()}"
|
174
|
+
f"Answer keys must be in {acceptable_answer_keys}, but got {value.keys()}."
|
147
175
|
)
|
148
176
|
if any([key not in value.keys() for key in acceptable_answer_keys]):
|
149
177
|
raise QuestionAnswerValidationError(
|
150
|
-
f"Answer must have all keys in {acceptable_answer_keys}, but got {value.keys()}"
|
178
|
+
f"Answer must have all keys in {acceptable_answer_keys}, but got {value.keys()}."
|
151
179
|
)
|
152
180
|
|
153
|
-
def
|
154
|
-
"""
|
155
|
-
|
181
|
+
def _validate_answer_list(self, answer: dict[str, Union[list, str]]) -> None:
|
182
|
+
"""Validate QuestionList-specific answer.
|
183
|
+
|
184
|
+
Check that answer["answer"]:
|
156
185
|
- is not empty, if `allow_nonresponse` is False
|
157
186
|
- has no more than `max_list_items` elements
|
158
187
|
- has no empty strings
|
@@ -177,13 +206,19 @@ class AnswerValidatorMixin:
|
|
177
206
|
f"Answer cannot contain empty strings, but got {value}."
|
178
207
|
)
|
179
208
|
|
180
|
-
def
|
181
|
-
"""
|
182
|
-
|
209
|
+
def _validate_answer_numerical(self, answer: dict) -> None:
|
210
|
+
"""Validate QuestionNumerical-specific answer.
|
211
|
+
|
212
|
+
Check that answer["answer"]:
|
183
213
|
- is not less than `min_value`
|
184
214
|
- is not greater than `max_value`
|
185
215
|
"""
|
186
|
-
|
216
|
+
try:
|
217
|
+
value = float(answer.get("answer"))
|
218
|
+
except ValueError:
|
219
|
+
raise QuestionAnswerValidationError(
|
220
|
+
f"Answer must real number or convertible to a real number (got {answer.get('answer')})."
|
221
|
+
)
|
187
222
|
if self.min_value is not None and value < self.min_value:
|
188
223
|
raise QuestionAnswerValidationError(
|
189
224
|
f"Value {value} is less than {self.min_value}"
|
@@ -194,11 +229,12 @@ class AnswerValidatorMixin:
|
|
194
229
|
)
|
195
230
|
return value
|
196
231
|
|
197
|
-
def
|
232
|
+
def _validate_answer_multiple_choice(
|
198
233
|
self, answer: dict[str, Union[str, int]]
|
199
234
|
) -> None:
|
200
|
-
"""
|
201
|
-
|
235
|
+
"""Validate QuestionMultipleChoice-specific answer.
|
236
|
+
|
237
|
+
Check that answer["answer"]:
|
202
238
|
- is a string, bytes-like object or real number
|
203
239
|
- is a non-negative integer
|
204
240
|
- is in the range of the number of options
|
@@ -218,9 +254,10 @@ class AnswerValidatorMixin:
|
|
218
254
|
f"Answer code {value} must be in {list(range(len(self.question_options)))}."
|
219
255
|
)
|
220
256
|
|
221
|
-
def
|
222
|
-
"""
|
223
|
-
|
257
|
+
def _validate_answer_rank(self, answer: dict[str, Union[str, int]]) -> None:
|
258
|
+
"""Validate QuestionRank-specific answer.
|
259
|
+
|
260
|
+
Check that answer["answer"]:
|
224
261
|
- contains only integers
|
225
262
|
- contains only integers in the range of the number of options
|
226
263
|
- has the correct number of elements
|
@@ -246,3 +283,81 @@ class AnswerValidatorMixin:
|
|
246
283
|
raise QuestionAnswerValidationError(
|
247
284
|
f"Rank answer {value}, but exactly {self.num_selections} selections required."
|
248
285
|
)
|
286
|
+
|
287
|
+
def _validate_answer_matrix(self, answer: dict[str, Any]) -> None:
|
288
|
+
"""Validate QuestionMatrix-specific answer.
|
289
|
+
|
290
|
+
Check that answer["answer"]:
|
291
|
+
- is a dictionary
|
292
|
+
- has all required question_items as keys
|
293
|
+
- has values that are valid options from question_options
|
294
|
+
- has the correct number of responses (one per item)
|
295
|
+
"""
|
296
|
+
value = answer.get("answer")
|
297
|
+
|
298
|
+
# Check that answer is a dictionary
|
299
|
+
if not isinstance(value, dict):
|
300
|
+
raise QuestionAnswerValidationError(
|
301
|
+
f"Matrix answer must be a dictionary mapping items to responses (got {value})"
|
302
|
+
)
|
303
|
+
|
304
|
+
# Check that all required items are present
|
305
|
+
required_items = set(self.question_items)
|
306
|
+
provided_items = set(value.keys())
|
307
|
+
|
308
|
+
if missing_items := (required_items - provided_items):
|
309
|
+
raise QuestionAnswerValidationError(
|
310
|
+
f"Missing responses for items: {missing_items}"
|
311
|
+
)
|
312
|
+
|
313
|
+
if extra_items := (provided_items - required_items):
|
314
|
+
raise QuestionAnswerValidationError(
|
315
|
+
f"Unexpected responses for items: {extra_items}"
|
316
|
+
)
|
317
|
+
|
318
|
+
# Check that all responses are valid options
|
319
|
+
valid_options = set(self.question_options)
|
320
|
+
for item, response in value.items():
|
321
|
+
if response not in valid_options:
|
322
|
+
raise QuestionAnswerValidationError(
|
323
|
+
f"Invalid response '{response}' for item '{item}'. "
|
324
|
+
f"Must be one of: {valid_options}"
|
325
|
+
)
|
326
|
+
|
327
|
+
def _validate_answer_dict(self, answer: dict[str, Any]) -> None:
|
328
|
+
"""Validate QuestionDict-specific answer.
|
329
|
+
|
330
|
+
Check that answer["answer"]:
|
331
|
+
- is a dictionary
|
332
|
+
- has all required answer_keys as keys
|
333
|
+
"""
|
334
|
+
value = answer.get("answer")
|
335
|
+
|
336
|
+
# Check that answer is a dictionary
|
337
|
+
if not isinstance(value, dict):
|
338
|
+
raise QuestionAnswerValidationError(
|
339
|
+
f"Dict answer must be a dictionary mapping values to specified keys (got {value})"
|
340
|
+
)
|
341
|
+
|
342
|
+
# Check that all required answer keys are present
|
343
|
+
required_keys = set(self.answer_keys)
|
344
|
+
provided_keys = set(value.keys())
|
345
|
+
|
346
|
+
if missing_keys := (required_keys - provided_keys):
|
347
|
+
raise QuestionAnswerValidationError(
|
348
|
+
f"Missing required keys: {missing_keys}"
|
349
|
+
)
|
350
|
+
|
351
|
+
if extra_keys := (provided_keys - required_keys):
|
352
|
+
raise QuestionAnswerValidationError(
|
353
|
+
f"Unexpected keys: {extra_keys}"
|
354
|
+
)
|
355
|
+
|
356
|
+
|
357
|
+
if __name__ == "__main__":
|
358
|
+
pass
|
359
|
+
# import doctest
|
360
|
+
|
361
|
+
# doctest.testmod(optionflags=doctest.ELLIPSIS)
|
362
|
+
|
363
|
+
# results = AnswerValidatorMixin().failing_job()
|
@@ -1,17 +1,20 @@
|
|
1
|
-
|
2
|
-
|
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
|
3
5
|
from edsl.scenarios.Scenario import Scenario
|
4
6
|
|
5
7
|
|
6
8
|
def compose_questions(
|
7
|
-
q1:
|
9
|
+
q1: QuestionBase, q2: QuestionBase, question_name: str = None
|
8
10
|
) -> QuestionFunctional:
|
9
11
|
"""
|
10
|
-
|
12
|
+
Compose two questions where the answer to q1 is used as an input to q2.
|
13
|
+
|
11
14
|
The resulting question is a question that can be used like other questions.
|
12
|
-
Note:
|
13
|
-
-
|
14
|
-
|
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
|
15
18
|
"""
|
16
19
|
if question_name is None:
|
17
20
|
question_name = f"{q1.question_name}_{q2.question_name}"
|
@@ -24,7 +27,10 @@ def compose_questions(
|
|
24
27
|
def combo(
|
25
28
|
scenario: Scenario, agent_traits: dict[str, str] = None
|
26
29
|
) -> QuestionFunctional:
|
30
|
+
"""Return the answer to the second question given the answer to the first question."""
|
27
31
|
# get the answer to the first question
|
32
|
+
from edsl.agents.Agent import Agent
|
33
|
+
|
28
34
|
first_answer = (
|
29
35
|
q1.by(scenario)
|
30
36
|
.by(Agent(traits=agent_traits))
|
@@ -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]
|
@@ -0,0 +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
|
@@ -2,22 +2,11 @@ from __future__ import annotations
|
|
2
2
|
from typing import Optional
|
3
3
|
from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
|
4
4
|
|
5
|
+
from edsl.questions.decorators import inject_exception
|
5
6
|
|
6
|
-
class QuestionLikertFive(QuestionMultipleChoice):
|
7
|
-
"""
|
8
|
-
This question asks the user to respond to a statement on a 5-point Likert scale.
|
9
|
-
|
10
|
-
Arguments:
|
11
|
-
- `question_name` is the name of the question (string)
|
12
|
-
- `question_text` is the text of the question (string)
|
13
7
|
|
14
|
-
|
15
|
-
|
16
|
-
- `question_options` are the options the user should select from (list of strings). If not provided, the default likert options are used. To view them, run `QuestionLikertFive.likert_options`
|
17
|
-
- `short_names_dict` maps question_options to short names (dictionary mapping strings to strings)
|
18
|
-
|
19
|
-
For an example, see `QuestionLikertFive.example()`
|
20
|
-
"""
|
8
|
+
class QuestionLikertFive(QuestionMultipleChoice):
|
9
|
+
"""This question prompts the agent to respond to a statement on a 5-point Likert scale."""
|
21
10
|
|
22
11
|
question_type = "likert_five"
|
23
12
|
likert_options: list[str] = [
|
@@ -27,27 +16,36 @@ class QuestionLikertFive(QuestionMultipleChoice):
|
|
27
16
|
"Agree",
|
28
17
|
"Strongly agree",
|
29
18
|
]
|
30
|
-
# default_instructions = QuestionMultipleChoice.default_instructions
|
31
19
|
|
32
20
|
def __init__(
|
33
21
|
self,
|
34
22
|
question_name: str,
|
35
23
|
question_text: str,
|
36
24
|
question_options: Optional[list[str]] = likert_options,
|
37
|
-
|
25
|
+
answering_instructions: Optional[str] = None,
|
26
|
+
question_presentation: Optional[str] = None,
|
27
|
+
include_comment: bool = True,
|
38
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
|
+
"""
|
39
35
|
super().__init__(
|
40
36
|
question_name=question_name,
|
41
37
|
question_text=question_text,
|
42
38
|
question_options=question_options,
|
43
|
-
|
39
|
+
use_code=False,
|
40
|
+
include_comment=include_comment,
|
41
|
+
answering_instructions=answering_instructions,
|
42
|
+
question_presentation=question_presentation,
|
44
43
|
)
|
45
44
|
|
46
|
-
################
|
47
|
-
# Helpful
|
48
|
-
################
|
49
45
|
@classmethod
|
46
|
+
@inject_exception
|
50
47
|
def example(cls) -> QuestionLikertFive:
|
48
|
+
"""Return an example question."""
|
51
49
|
return cls(
|
52
50
|
question_name="happy_raining",
|
53
51
|
question_text="I'm only happy when it rains.",
|
@@ -55,20 +53,24 @@ class QuestionLikertFive(QuestionMultipleChoice):
|
|
55
53
|
|
56
54
|
|
57
55
|
def main():
|
56
|
+
"""Test QuestionLikertFive."""
|
58
57
|
from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
|
59
58
|
|
60
59
|
q = QuestionLikertFive.example()
|
61
60
|
q.question_text
|
62
61
|
q.question_options
|
63
62
|
q.question_name
|
64
|
-
q.short_names_dict
|
65
63
|
# validate an answer
|
66
|
-
q.
|
64
|
+
q._validate_answer({"answer": 0, "comment": "I like custard"})
|
67
65
|
# translate answer code
|
68
|
-
q.
|
69
|
-
q.
|
70
|
-
q.
|
71
|
-
q.
|
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))
|
72
70
|
# serialization (inherits from Question)
|
73
71
|
q.to_dict()
|
74
72
|
assert q.from_dict(q.to_dict()) == q
|
73
|
+
|
74
|
+
import doctest
|
75
|
+
|
76
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|