edsl 0.1.15__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 +45 -10
- edsl/__version__.py +1 -1
- 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 +115 -113
- 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 -206
- 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.15.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 -435
- edsl/jobs/JobsRunner.py +0 -63
- edsl/jobs/JobsRunnerStatusMixin.py +0 -115
- edsl/jobs/base.py +0 -47
- edsl/jobs/buckets.py +0 -178
- edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
- edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
- edsl/jobs/task_management.py +0 -215
- 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.15.dist-info/METADATA +0 -69
- edsl-0.1.15.dist-info/RECORD +0 -142
- /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.15.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -1,32 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
3
|
-
|
4
|
-
|
5
|
-
class CheckBox(QuestionInstuctionsBase):
|
6
|
-
question_type = "checkbox"
|
7
|
-
model = "gpt-4-1106-preview"
|
8
|
-
default_instructions = textwrap.dedent(
|
9
|
-
"""\
|
10
|
-
You are being asked the following question: {{question_text}}
|
11
|
-
The options are
|
12
|
-
{% for option in question_options %}
|
13
|
-
{{ loop.index0 }}: {{option}}
|
14
|
-
{% endfor %}
|
15
|
-
Return a valid JSON formatted like this, selecting only the number of the option:
|
16
|
-
{"answer": [<put comma-separated list of answer codes here>], "comment": "<put explanation here>"}
|
17
|
-
{% if min_selections != None and max_selections != None and min_selections == max_selections %}
|
18
|
-
You must select exactly {{min_selections}} options.
|
19
|
-
{% elif min_selections != None and max_selections != None %}
|
20
|
-
Minimum number of options that must be selected: {{min_selections}}.
|
21
|
-
Maximum number of options that must be selected: {{max_selections}}.
|
22
|
-
{% elif min_selections != None %}
|
23
|
-
Minimum number of options that must be selected: {{min_selections}}.
|
24
|
-
{% elif max_selections != None %}
|
25
|
-
Maximum number of options that must be selected: {{max_selections}}.
|
26
|
-
{% endif %}
|
27
|
-
"""
|
28
|
-
)
|
29
|
-
|
30
|
-
|
31
|
-
class TopK(CheckBox):
|
32
|
-
question_type = "top_k"
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
|
3
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
4
|
-
|
5
|
-
|
6
|
-
class Extract(QuestionInstuctionsBase):
|
7
|
-
question_type = "extract"
|
8
|
-
model = "gpt-4-1106-preview"
|
9
|
-
default_instructions = textwrap.dedent(
|
10
|
-
"""\
|
11
|
-
You are given the following input: "{{question_text}}".
|
12
|
-
Create an ANSWER should be formatted like this: "{{ answer_template }}",
|
13
|
-
and it should have the same keys but values extracted from the input.
|
14
|
-
If the value of a key is not present in the input, fill with "null".
|
15
|
-
Return a valid JSON formatted like this:
|
16
|
-
{"answer": <put your ANSWER here>}
|
17
|
-
ONLY RETURN THE JSON, AND NOTHING ELSE.
|
18
|
-
"""
|
19
|
-
)
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
3
|
-
|
4
|
-
|
5
|
-
class FreeText(QuestionInstuctionsBase):
|
6
|
-
question_type = "free_text"
|
7
|
-
model = "gpt-4-1106-preview"
|
8
|
-
default_instructions = textwrap.dedent(
|
9
|
-
"""\
|
10
|
-
You are being asked the following question: {{question_text}}
|
11
|
-
Return a valid JSON formatted like this:
|
12
|
-
{"answer": "<put free text answer here>"}
|
13
|
-
"""
|
14
|
-
)
|
@@ -1,20 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
|
3
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
4
|
-
|
5
|
-
|
6
|
-
class LinearScale(QuestionInstuctionsBase):
|
7
|
-
question_type = "linear_scale"
|
8
|
-
model = "gpt-4-1106-preview"
|
9
|
-
default_instructions = textwrap.dedent(
|
10
|
-
"""\
|
11
|
-
You are being asked the following question: {{question_text}}
|
12
|
-
The options are
|
13
|
-
{% for option in question_options %}
|
14
|
-
{{ loop.index0 }}: {{option}}
|
15
|
-
{% endfor %}
|
16
|
-
Return a valid JSON formatted like this, selecting only the code of the option (codes start at 0):
|
17
|
-
{"answer": <put answer code here>, "comment": "<put explanation here>"}
|
18
|
-
Only 1 option may be selected.
|
19
|
-
"""
|
20
|
-
)
|
@@ -1,22 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
|
3
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
4
|
-
|
5
|
-
|
6
|
-
class ListQuestion(QuestionInstuctionsBase):
|
7
|
-
question_type = "list"
|
8
|
-
model = "gpt-4-1106-preview"
|
9
|
-
default_instructions = textwrap.dedent(
|
10
|
-
"""\
|
11
|
-
{{question_text}}
|
12
|
-
|
13
|
-
Your response should be only a valid JSON in the following format:
|
14
|
-
{
|
15
|
-
"answer": [<comma-separated list of responsive words or phrases as independent strings>],
|
16
|
-
"comment": "<put comment here>"
|
17
|
-
}
|
18
|
-
{% if max_list_items is not none %}
|
19
|
-
The list must not contain more than {{ max_list_items }} items.
|
20
|
-
{% endif %}
|
21
|
-
"""
|
22
|
-
)
|
@@ -1,44 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
3
|
-
|
4
|
-
|
5
|
-
class MultipleChoiceTurbo(QuestionInstuctionsBase):
|
6
|
-
question_type = "multiple_choice"
|
7
|
-
model = "gpt-3.5-turbo"
|
8
|
-
default_instructions = textwrap.dedent(
|
9
|
-
"""\
|
10
|
-
You are being asked the following question: {{question_text}}
|
11
|
-
The options are
|
12
|
-
{% for option in question_options %}
|
13
|
-
{{ loop.index0 }}: {{option}}
|
14
|
-
{% endfor %}
|
15
|
-
Return a valid JSON formatted like this, selecting only the number of the option:
|
16
|
-
{"answer": <put answer code here>, "comment": "<put explanation here>"}
|
17
|
-
Only 1 option may be selected.
|
18
|
-
"""
|
19
|
-
)
|
20
|
-
|
21
|
-
|
22
|
-
class MultipleChoice(QuestionInstuctionsBase):
|
23
|
-
question_type = "multiple_choice"
|
24
|
-
model = "gpt-4-1106-preview"
|
25
|
-
default_instructions = textwrap.dedent(
|
26
|
-
"""\
|
27
|
-
You are being asked the following question: {{question_text}}
|
28
|
-
The options are
|
29
|
-
{% for option in question_options %}
|
30
|
-
{{ loop.index0 }}: {{option}}
|
31
|
-
{% endfor %}
|
32
|
-
Return a valid JSON formatted like this, selecting only the number of the option:
|
33
|
-
{"answer": <put answer code here>, "comment": "<put explanation here>"}
|
34
|
-
Only 1 option may be selected.
|
35
|
-
"""
|
36
|
-
)
|
37
|
-
|
38
|
-
|
39
|
-
class LikertFive(MultipleChoice):
|
40
|
-
question_type = "likert_five"
|
41
|
-
|
42
|
-
|
43
|
-
class YesNo(MultipleChoice):
|
44
|
-
question_type = "yes_no"
|
@@ -1,31 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
|
3
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
4
|
-
|
5
|
-
|
6
|
-
class Numerical(QuestionInstuctionsBase):
|
7
|
-
question_type = "numerical"
|
8
|
-
model = "gpt-4-1106-preview"
|
9
|
-
default_instructions = textwrap.dedent(
|
10
|
-
"""\
|
11
|
-
You are being asked a question that requires a numerical response
|
12
|
-
in the form of an integer or decimal (e.g., -12, 0, 1, 2, 3.45, ...).
|
13
|
-
Your response must be in the following format:
|
14
|
-
{"answer": "<your numerical answer here>", "comment": "<your explanation here"}
|
15
|
-
You must only include an integer or decimal in the quoted "answer" part of your response.
|
16
|
-
Here is an example of a valid response:
|
17
|
-
{"answer": "100", "comment": "This is my explanation..."}
|
18
|
-
Here is an example of a response that is invalid because the "answer" includes words:
|
19
|
-
{"answer": "I don't know.", "comment": "This is my explanation..."}
|
20
|
-
If your response is equivalent to zero, your formatted response should look like this:
|
21
|
-
{"answer": "0", "comment": "This is my explanation..."}
|
22
|
-
|
23
|
-
You are being asked the following question: {{question_text}}
|
24
|
-
{% if min_value is not none %}
|
25
|
-
Minimum answer value: {{min_value}}
|
26
|
-
{% endif %}
|
27
|
-
{% if max_value is not none %}
|
28
|
-
Maximum answer value: {{max_value}}
|
29
|
-
{% endif %}
|
30
|
-
"""
|
31
|
-
)
|
@@ -1,21 +0,0 @@
|
|
1
|
-
import textwrap
|
2
|
-
|
3
|
-
from edsl.prompts.QuestionInstructionsBase import QuestionInstuctionsBase
|
4
|
-
|
5
|
-
|
6
|
-
class Rank(QuestionInstuctionsBase):
|
7
|
-
question_type = "rank"
|
8
|
-
model = "gpt-4-1106-preview"
|
9
|
-
default_instructions = textwrap.dedent(
|
10
|
-
"""\
|
11
|
-
You are being asked the following question: {{question_text}}
|
12
|
-
The options are
|
13
|
-
{% for option in question_options %}
|
14
|
-
{{ loop.index0 }}: {{option}}
|
15
|
-
{% endfor %}
|
16
|
-
Return a valid JSON formatted like this, selecting the numbers of the options in order of preference,
|
17
|
-
with the most preferred option first, and the least preferred option last:
|
18
|
-
{"answer": [<put comma-separated list of answer codes here>], "comment": "<put explanation here>"}
|
19
|
-
Exactly {{num_selections}} options must be selected.
|
20
|
-
"""
|
21
|
-
)
|
edsl/prompts/prompt_config.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
|
3
|
-
NEGATIVE_INFINITY = float("-inf")
|
4
|
-
|
5
|
-
|
6
|
-
class AttributeTypes(Enum):
|
7
|
-
COMPONENT_TYPE = "component_type"
|
8
|
-
MODEL = "model"
|
9
|
-
QUESTION_TYPE = "question_type"
|
10
|
-
|
11
|
-
|
12
|
-
class ComponentTypes(Enum):
|
13
|
-
"""The types of attributes that a prompt can have"""
|
14
|
-
|
15
|
-
TEST = "test"
|
16
|
-
GENERIC = "generic"
|
17
|
-
QUESTION_DATA = "question_data"
|
18
|
-
QUESTION_INSTRUCTIONS = "question_instructions"
|
19
|
-
AGENT_INSTRUCTIONS = "agent_instructions"
|
20
|
-
AGENT_PERSONA = "agent_persona"
|
21
|
-
SURVEY_INSTRUCTIONS = "survey_instructions"
|
22
|
-
SURVEY_DATA = "survey_data"
|
23
|
-
|
24
|
-
|
25
|
-
names_to_component_types = {v.value: v for k, v in ComponentTypes.__members__.items()}
|
26
|
-
|
27
|
-
C2A = {
|
28
|
-
ComponentTypes.QUESTION_INSTRUCTIONS: [
|
29
|
-
AttributeTypes.QUESTION_TYPE,
|
30
|
-
AttributeTypes.MODEL,
|
31
|
-
],
|
32
|
-
ComponentTypes.AGENT_INSTRUCTIONS: [AttributeTypes.MODEL],
|
33
|
-
}
|
edsl/prompts/registry.py
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
import traceback
|
2
|
-
from collections import defaultdict
|
3
|
-
from typing import List, Any
|
4
|
-
|
5
|
-
from abc import ABCMeta, abstractmethod
|
6
|
-
|
7
|
-
from edsl.prompts.prompt_config import (
|
8
|
-
C2A,
|
9
|
-
names_to_component_types,
|
10
|
-
ComponentTypes,
|
11
|
-
NEGATIVE_INFINITY,
|
12
|
-
)
|
13
|
-
|
14
|
-
from edsl.enums import QuestionType, LanguageModelType
|
15
|
-
|
16
|
-
from edsl.exceptions.prompts import (
|
17
|
-
PromptBadQuestionTypeError,
|
18
|
-
PromptBadLanguageModelTypeError,
|
19
|
-
)
|
20
|
-
|
21
|
-
|
22
|
-
class RegisterPromptsMeta(ABCMeta):
|
23
|
-
"Metaclass to register prompts"
|
24
|
-
_registry = defaultdict(list) # Initialize the registry as a dictionary
|
25
|
-
_prompts_by_component_type = defaultdict(list)
|
26
|
-
# _instances = {}
|
27
|
-
|
28
|
-
# def __new__(mcs, name, bases, dct):
|
29
|
-
# if mcs not in mcs._instances:
|
30
|
-
# mcs._instances[mcs] = super(RegisterPromptsMeta, mcs).__new__(
|
31
|
-
# mcs, name, bases, dct
|
32
|
-
# )
|
33
|
-
# return mcs._instances[mcs]
|
34
|
-
|
35
|
-
def __init__(cls, name, bases, dct):
|
36
|
-
"""
|
37
|
-
We can only have one prompt class per name.
|
38
|
-
Each prompt class must have a component type from the ComponentTypes enum.
|
39
|
-
|
40
|
-
>>> class Prompt1(PromptBase):
|
41
|
-
... component_type = ComponentTypes.TEST
|
42
|
-
|
43
|
-
>>> class Prompt1(PromptBase):
|
44
|
-
... component_type = ComponentTypes.TEST
|
45
|
-
Traceback (most recent call last):
|
46
|
-
...
|
47
|
-
Exception: We already have a Prompt class named Prompt1.
|
48
|
-
"""
|
49
|
-
super(RegisterPromptsMeta, cls).__init__(name, bases, dct)
|
50
|
-
# print(f"Current state of registry: {RegisterPromptsMeta._registry}")
|
51
|
-
# print(f"Registry called with {name}")
|
52
|
-
if "Base" in name or name == "Prompt":
|
53
|
-
# print("Exiting")
|
54
|
-
return None # We don't want to register the base class
|
55
|
-
|
56
|
-
if name in RegisterPromptsMeta._registry:
|
57
|
-
if RegisterPromptsMeta._registry[name] != cls:
|
58
|
-
raise Exception(f"We already have a Prompt class named {name}.")
|
59
|
-
else:
|
60
|
-
# print("It's the same thing - it's fine.")
|
61
|
-
return None
|
62
|
-
|
63
|
-
RegisterPromptsMeta._registry[name] = cls
|
64
|
-
# print(f"Current registry: {RegisterPromptsMeta._registry}")
|
65
|
-
if (
|
66
|
-
component_type := getattr(cls, "component_type", None)
|
67
|
-
) not in ComponentTypes:
|
68
|
-
raise Exception(f"Prompt {name} is not in the list of component types")
|
69
|
-
|
70
|
-
## Make sure that the prompt has a question_type class attribute & it's valid
|
71
|
-
if component_type == ComponentTypes.QUESTION_INSTRUCTIONS:
|
72
|
-
if not hasattr(cls, "question_type"):
|
73
|
-
raise PromptBadQuestionTypeError(
|
74
|
-
"A QuestionInstructions prompt must has a question_type value"
|
75
|
-
)
|
76
|
-
if not QuestionType.is_value_valid(cls.question_type):
|
77
|
-
acceptable_values = [item.value for item in QuestionType]
|
78
|
-
raise PromptBadQuestionTypeError(
|
79
|
-
f"""
|
80
|
-
A Prompt's question_type must be one of {QuestionType} values, which are
|
81
|
-
currently {acceptable_values}. You passed {cls.question_type}."""
|
82
|
-
)
|
83
|
-
|
84
|
-
## Make sure that if the prompt has a model class attribute, it's valid
|
85
|
-
if hasattr(cls, "model"):
|
86
|
-
if not LanguageModelType.is_value_valid(cls.model):
|
87
|
-
acceptable_values = [item.value for item in LanguageModelType]
|
88
|
-
raise PromptBadLanguageModelTypeError(
|
89
|
-
f"""
|
90
|
-
A Prompt's model must be one of {LanguageModelType} values, which are
|
91
|
-
currently {acceptable_values}. You passed {cls.model}."""
|
92
|
-
)
|
93
|
-
|
94
|
-
key = cls._create_prompt_class_key(dct, component_type)
|
95
|
-
cls.data = key
|
96
|
-
RegisterPromptsMeta._prompts_by_component_type[component_type].append(cls)
|
97
|
-
|
98
|
-
@classmethod
|
99
|
-
def _create_prompt_class_key(cls, dct, component_type) -> tuple[tuple[str, Any]]:
|
100
|
-
attributes = [attribute.value for attribute in C2A.get(component_type, [])]
|
101
|
-
cls_data = {key: value for key, value in dct.items() if key in attributes}
|
102
|
-
return tuple(cls_data.items())
|
103
|
-
|
104
|
-
@classmethod
|
105
|
-
def _get_classes_with_scores(cls, **kwargs) -> List[tuple[float, "PromptBase"]]:
|
106
|
-
"""
|
107
|
-
This how we find matching prompts.
|
108
|
-
NB that _get_classes_with_scores returns a list of tuples.
|
109
|
-
The first element of the tuple is the score, and the second element is the prompt class.
|
110
|
-
There is a public-facing function called get_classes that returns only the prompt classes.
|
111
|
-
|
112
|
-
The kwargs are the attributes that we want to match on. E.g., supposed you
|
113
|
-
wanted a prompt with component_type = "question_instructions" and question_type = "multiple_choice".
|
114
|
-
You would run:
|
115
|
-
|
116
|
-
>>> get_classes(component_type="question_instructions", question_type="multiple_choice", model="gpt-4-1106-preview")
|
117
|
-
[<class '__main__.MultipleChoice'>, <class '__main__.MultipleChoiceTurbo'>]
|
118
|
-
|
119
|
-
In the above example, we have two prompts that match. Note that the order of the prompts is determined by the score and the regular MultipleChoice
|
120
|
-
is ranked higher because it matches on the model as well.
|
121
|
-
|
122
|
-
Scores are computed by the _score method. The score is the number of attributes that match, with their weights.
|
123
|
-
However, if a required attribute doesn't match, then the score is -inf and it can never be selected.
|
124
|
-
|
125
|
-
The function will throw an exception if you don't specify a component type that's in the ComponentTypes enum.
|
126
|
-
|
127
|
-
>>> get_classes(component_type="chicken_tenders", question_type="multiple_choice")
|
128
|
-
Traceback (most recent call last):
|
129
|
-
...
|
130
|
-
Exception: You must specify a component type. It must be one of dict_keys([...])
|
131
|
-
|
132
|
-
>>> get_classes(component_type="generic")
|
133
|
-
[]
|
134
|
-
"""
|
135
|
-
component_type_string = kwargs.get("component_type", None)
|
136
|
-
component_type = names_to_component_types.get(component_type_string, None)
|
137
|
-
|
138
|
-
if component_type is None:
|
139
|
-
raise Exception(
|
140
|
-
f"You must specify a component type. It must be one of {names_to_component_types.keys()}"
|
141
|
-
)
|
142
|
-
|
143
|
-
try:
|
144
|
-
prompts = cls._prompts_by_component_type[component_type]
|
145
|
-
except KeyError:
|
146
|
-
raise Exception(f"No prompts for component type {component_type}")
|
147
|
-
|
148
|
-
with_scores = [(cls._score(kwargs, prompt), prompt) for prompt in prompts]
|
149
|
-
with_scores = sorted(with_scores, key=lambda x: -x[0])
|
150
|
-
# filter out the ones with -inf
|
151
|
-
matches_with_scores = cls._filter_out_non_matches(with_scores)
|
152
|
-
return matches_with_scores
|
153
|
-
|
154
|
-
@classmethod
|
155
|
-
def _filter_out_non_matches(cls, prompts_with_scores):
|
156
|
-
return [
|
157
|
-
(score, prompt)
|
158
|
-
for score, prompt in prompts_with_scores
|
159
|
-
if score > NEGATIVE_INFINITY
|
160
|
-
]
|
161
|
-
|
162
|
-
@classmethod
|
163
|
-
def get_classes(cls, **kwargs):
|
164
|
-
"Public-facing function that returns only the prompt classes and not the scores."
|
165
|
-
with_scores = cls._get_classes_with_scores(**kwargs)
|
166
|
-
return [prompt for _, prompt in with_scores]
|
167
|
-
|
168
|
-
@classmethod
|
169
|
-
def _score(cls, kwargs, prompt):
|
170
|
-
required_list = ["question_type"]
|
171
|
-
score = 0
|
172
|
-
for key, value in kwargs.items():
|
173
|
-
if prompt_value := getattr(prompt, key, None) == value:
|
174
|
-
score += 1
|
175
|
-
else:
|
176
|
-
if key in required_list:
|
177
|
-
score += NEGATIVE_INFINITY
|
178
|
-
return score
|
179
|
-
|
180
|
-
@classmethod
|
181
|
-
def get_registered_classes(cls):
|
182
|
-
return cls._registry
|
183
|
-
|
184
|
-
|
185
|
-
get_classes = RegisterPromptsMeta.get_classes
|
edsl/questions/Question.py
DELETED
@@ -1,240 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
import textwrap
|
3
|
-
import io
|
4
|
-
from abc import ABC, abstractmethod, ABCMeta
|
5
|
-
from rich.console import Console
|
6
|
-
from rich.table import Table
|
7
|
-
from jinja2 import Template, Environment, meta
|
8
|
-
from typing import Any, Type
|
9
|
-
from edsl.exceptions import (
|
10
|
-
QuestionResponseValidationError,
|
11
|
-
QuestionSerializationError,
|
12
|
-
QuestionScenarioRenderError,
|
13
|
-
)
|
14
|
-
from edsl.questions.descriptors import (
|
15
|
-
InstructionsDescriptor,
|
16
|
-
QuestionNameDescriptor,
|
17
|
-
QuestionTextDescriptor,
|
18
|
-
ShortNamesDictDescriptor,
|
19
|
-
)
|
20
|
-
|
21
|
-
from edsl.prompts.Prompt import Prompt
|
22
|
-
|
23
|
-
from edsl.enums import QuestionType
|
24
|
-
|
25
|
-
# from edsl.questions.question_registry import get_question_class
|
26
|
-
from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
|
27
|
-
|
28
|
-
from edsl.exceptions.questions import QuestionMissingTypeError, QuestionBadTypeError
|
29
|
-
|
30
|
-
|
31
|
-
class RegisterQuestionsMeta(ABCMeta):
|
32
|
-
"Metaclass to register output elements in a registry i.e., those that have a parent"
|
33
|
-
_registry = {} # Initialize the registry as a dictionary
|
34
|
-
|
35
|
-
def __init__(cls, name, bases, dct):
|
36
|
-
super(RegisterQuestionsMeta, cls).__init__(name, bases, dct)
|
37
|
-
if name != "Question":
|
38
|
-
## Enforce that all questions have a question_type class attribute
|
39
|
-
## and it comes from our enum of valid question types.
|
40
|
-
if not hasattr(cls, "question_type"):
|
41
|
-
raise QuestionMissingTypeError(
|
42
|
-
"Question must have a question_type class attribute"
|
43
|
-
)
|
44
|
-
|
45
|
-
if not QuestionType.is_value_valid(cls.question_type):
|
46
|
-
acceptable_values = [item.value for item in QuestionType]
|
47
|
-
raise QuestionBadTypeError(
|
48
|
-
f"""question_type must be one of {QuestionType} values, which are
|
49
|
-
currently {acceptable_values}"""
|
50
|
-
""
|
51
|
-
)
|
52
|
-
|
53
|
-
RegisterQuestionsMeta._registry[name] = cls
|
54
|
-
|
55
|
-
@classmethod
|
56
|
-
def get_registered_classes(cls):
|
57
|
-
return cls._registry
|
58
|
-
|
59
|
-
@classmethod
|
60
|
-
def question_types_to_classes(
|
61
|
-
cls,
|
62
|
-
):
|
63
|
-
d = {}
|
64
|
-
for classname, cls in cls._registry.items():
|
65
|
-
if hasattr(cls, "question_type"):
|
66
|
-
d[cls.question_type] = cls
|
67
|
-
else:
|
68
|
-
raise Exception(
|
69
|
-
f"Class {classname} does not have a question_type class attribute"
|
70
|
-
)
|
71
|
-
return d
|
72
|
-
|
73
|
-
|
74
|
-
from edsl.Base import PersistenceMixin, RichPrintingMixin
|
75
|
-
|
76
|
-
|
77
|
-
class Question(
|
78
|
-
PersistenceMixin,
|
79
|
-
RichPrintingMixin,
|
80
|
-
ABC,
|
81
|
-
AnswerValidatorMixin,
|
82
|
-
metaclass=RegisterQuestionsMeta,
|
83
|
-
):
|
84
|
-
"""
|
85
|
-
ABC for the Question class. All questions should inherit from this class.
|
86
|
-
"""
|
87
|
-
|
88
|
-
question_name: str = QuestionNameDescriptor()
|
89
|
-
question_text: str = QuestionTextDescriptor()
|
90
|
-
short_names_dict: dict[str, str] = ShortNamesDictDescriptor()
|
91
|
-
|
92
|
-
@property
|
93
|
-
def data(self) -> dict:
|
94
|
-
"""Returns a dictionary of question attributes **except** for question_type"""
|
95
|
-
candidate_data = {
|
96
|
-
k.replace("_", "", 1): v
|
97
|
-
for k, v in self.__dict__.items()
|
98
|
-
if k.startswith("_")
|
99
|
-
}
|
100
|
-
optional_attributes = {
|
101
|
-
"set_instructions": "instructions",
|
102
|
-
}
|
103
|
-
for boolean_flag, attribute in optional_attributes.items():
|
104
|
-
if hasattr(self, boolean_flag) and not getattr(self, boolean_flag):
|
105
|
-
candidate_data.pop(attribute, None)
|
106
|
-
|
107
|
-
return candidate_data
|
108
|
-
|
109
|
-
############################
|
110
|
-
# Serialization methods
|
111
|
-
############################
|
112
|
-
def to_dict(self) -> dict[str, Any]:
|
113
|
-
"""Converts the question to a dictionary that includes the question type (used in deserialization)."""
|
114
|
-
candidate_data = self.data.copy()
|
115
|
-
candidate_data["question_type"] = self.question_type
|
116
|
-
return candidate_data
|
117
|
-
|
118
|
-
@classmethod
|
119
|
-
def from_dict(cls, data: dict) -> Type[Question]:
|
120
|
-
"""Constructs a question object from a dictionary created by that question's `to_dict` method."""
|
121
|
-
local_data = data.copy()
|
122
|
-
try:
|
123
|
-
question_type = local_data.pop("question_type")
|
124
|
-
except:
|
125
|
-
raise QuestionSerializationError(
|
126
|
-
f"Data does not have a 'question_type' field (got {data})."
|
127
|
-
)
|
128
|
-
from edsl.questions.question_registry import get_question_class
|
129
|
-
|
130
|
-
try:
|
131
|
-
question_class = get_question_class(question_type)
|
132
|
-
except ValueError:
|
133
|
-
raise QuestionSerializationError(
|
134
|
-
f"No question registered with question_type {question_type}"
|
135
|
-
)
|
136
|
-
return question_class(**local_data)
|
137
|
-
|
138
|
-
############################
|
139
|
-
# Dunder methods
|
140
|
-
############################
|
141
|
-
def __repr__(self) -> str:
|
142
|
-
"""Returns a string representation of the question. Should be able to be used to reconstruct the question."""
|
143
|
-
class_name = self.__class__.__name__
|
144
|
-
items = [
|
145
|
-
f"{k} = '{v}'" if isinstance(v, str) else f"{k} = {v}"
|
146
|
-
for k, v in self.data.items()
|
147
|
-
if k != "question_type"
|
148
|
-
]
|
149
|
-
return f"{class_name}({', '.join(items)})"
|
150
|
-
|
151
|
-
def __eq__(self, other: Type[Question]) -> bool:
|
152
|
-
"""Checks if two questions are equal. Equality is defined as having the .to_dict()"""
|
153
|
-
if not isinstance(other, Question):
|
154
|
-
return False
|
155
|
-
return self.to_dict() == other.to_dict()
|
156
|
-
|
157
|
-
# TODO: Throws an error that should be addressed at QuestionFunctional
|
158
|
-
def __add__(self, other_question):
|
159
|
-
"""
|
160
|
-
Composes two questions into a single question.
|
161
|
-
>>> from edsl.scenarios.Scenario import Scenario
|
162
|
-
>>> from edsl.questions.QuestionFreeText import QuestionFreeText
|
163
|
-
>>> from edsl.questions.QuestionNumerical import QuestionNumerical
|
164
|
-
>>> q1 = QuestionFreeText(question_text = "What is the capital of {{country}}", question_name = "capital")
|
165
|
-
>>> q2 = QuestionNumerical(question_text = "What is the population of {{capital}}, in millions. Please round", question_name = "population")
|
166
|
-
>>> q3 = q1 + q2
|
167
|
-
"""
|
168
|
-
from edsl.questions import compose_questions
|
169
|
-
|
170
|
-
return compose_questions(self, other_question)
|
171
|
-
|
172
|
-
@abstractmethod
|
173
|
-
def validate_answer(self, answer: dict[str, str]):
|
174
|
-
pass
|
175
|
-
|
176
|
-
def validate_response(self, response):
|
177
|
-
"""Validates the response from the LLM"""
|
178
|
-
if "answer" not in response:
|
179
|
-
raise QuestionResponseValidationError(
|
180
|
-
"Response from LLM does not have an answer"
|
181
|
-
)
|
182
|
-
return response
|
183
|
-
|
184
|
-
@abstractmethod
|
185
|
-
def translate_answer_code_to_answer(self): # pragma: no cover
|
186
|
-
"""Translates the answer code to the actual answer. Behavior depends on the question type."""
|
187
|
-
pass
|
188
|
-
|
189
|
-
@abstractmethod
|
190
|
-
def simulate_answer(self, human_readable=True) -> dict: # pragma: no cover
|
191
|
-
"""Simulates a valid answer for debugging purposes (what the validator expects)"""
|
192
|
-
pass
|
193
|
-
|
194
|
-
############################
|
195
|
-
# Forward methods
|
196
|
-
############################
|
197
|
-
def add_question(self, other):
|
198
|
-
"Adds a question to this question by turning them into a survey with two questions"
|
199
|
-
from edsl.surveys.Survey import Survey
|
200
|
-
|
201
|
-
s = Survey([self, other])
|
202
|
-
return s
|
203
|
-
|
204
|
-
def run(self, *args, **kwargs):
|
205
|
-
"Turns a single question into a survey and runs it."
|
206
|
-
from edsl.surveys.Survey import Survey
|
207
|
-
|
208
|
-
s = Survey([self])
|
209
|
-
return s.run(*args, **kwargs)
|
210
|
-
|
211
|
-
def by(self, *args):
|
212
|
-
"Documentation missing."
|
213
|
-
from edsl.surveys.Survey import Survey
|
214
|
-
|
215
|
-
s = Survey([self])
|
216
|
-
return s.by(*args)
|
217
|
-
|
218
|
-
def rich_print(self):
|
219
|
-
table = Table(show_header=True, header_style="bold magenta")
|
220
|
-
table.add_column("Question Name", style="dim")
|
221
|
-
table.add_column("Question Type")
|
222
|
-
table.add_column("Question Text")
|
223
|
-
table.add_column("Options")
|
224
|
-
|
225
|
-
question = self
|
226
|
-
if hasattr(question, "question_options"):
|
227
|
-
options = ", ".join([str(o) for o in question.question_options])
|
228
|
-
else:
|
229
|
-
options = "None"
|
230
|
-
table.add_row(
|
231
|
-
question.question_name,
|
232
|
-
question.question_type,
|
233
|
-
question.question_text,
|
234
|
-
options,
|
235
|
-
)
|
236
|
-
return table
|
237
|
-
|
238
|
-
|
239
|
-
if __name__ == "__main__":
|
240
|
-
q = get_question_class("free_text")
|