edsl 0.1.46__py3-none-any.whl → 0.1.48__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/__init__.py +44 -39
- edsl/__version__.py +1 -1
- edsl/agents/__init__.py +4 -2
- edsl/agents/{Agent.py → agent.py} +442 -152
- edsl/agents/{AgentList.py → agent_list.py} +220 -162
- edsl/agents/descriptors.py +46 -7
- edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
- edsl/base/__init__.py +75 -0
- edsl/base/base_class.py +1303 -0
- edsl/base/data_transfer_models.py +114 -0
- edsl/base/enums.py +215 -0
- edsl/base.py +8 -0
- edsl/buckets/__init__.py +25 -0
- edsl/buckets/bucket_collection.py +324 -0
- edsl/buckets/model_buckets.py +206 -0
- edsl/buckets/token_bucket.py +502 -0
- edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
- edsl/buckets/token_bucket_client.py +509 -0
- edsl/caching/__init__.py +20 -0
- edsl/caching/cache.py +814 -0
- edsl/caching/cache_entry.py +427 -0
- edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
- edsl/caching/exceptions.py +24 -0
- edsl/caching/orm.py +30 -0
- edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
- edsl/caching/sql_dict.py +441 -0
- edsl/config/__init__.py +8 -0
- edsl/config/config_class.py +177 -0
- edsl/config.py +4 -176
- edsl/conversation/Conversation.py +7 -7
- edsl/conversation/car_buying.py +4 -4
- edsl/conversation/chips.py +6 -6
- edsl/coop/__init__.py +25 -2
- edsl/coop/coop.py +430 -113
- edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
- edsl/coop/exceptions.py +62 -0
- edsl/coop/price_fetcher.py +126 -0
- edsl/coop/utils.py +89 -24
- edsl/data_transfer_models.py +5 -72
- edsl/dataset/__init__.py +10 -0
- edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
- edsl/dataset/dataset_operations_mixin.py +1492 -0
- edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
- edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
- edsl/{results → dataset/display}/table_renderers.py +58 -2
- edsl/{results → dataset}/file_exports.py +4 -5
- edsl/{results → dataset}/smart_objects.py +2 -2
- edsl/enums.py +5 -205
- edsl/inference_services/__init__.py +5 -0
- edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
- edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
- edsl/inference_services/data_structures.py +3 -2
- edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
- edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
- edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
- edsl/inference_services/registry.py +4 -41
- edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
- edsl/inference_services/services/__init__.py +31 -0
- edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
- edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
- edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
- edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
- edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
- edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
- edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
- edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
- edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
- edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
- edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +12 -12
- edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
- edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
- edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
- edsl/inference_services/write_available.py +1 -2
- edsl/instructions/__init__.py +6 -0
- edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
- edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
- edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
- edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
- edsl/interviews/__init__.py +4 -0
- edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
- edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
- edsl/interviews/interview.py +638 -0
- edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
- edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
- edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
- edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
- edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
- edsl/invigilators/__init__.py +38 -0
- edsl/invigilators/invigilator_base.py +477 -0
- edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
- edsl/invigilators/prompt_constructor.py +476 -0
- edsl/{agents → invigilators}/prompt_helpers.py +2 -1
- edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
- edsl/{agents → invigilators}/question_option_processor.py +96 -21
- edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
- edsl/jobs/__init__.py +7 -1
- edsl/jobs/async_interview_runner.py +99 -35
- edsl/jobs/check_survey_scenario_compatibility.py +7 -5
- edsl/jobs/data_structures.py +153 -22
- edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
- edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
- edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
- edsl/jobs/{Jobs.py → jobs.py} +321 -155
- edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
- edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +20 -17
- edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
- edsl/jobs/jobs_pricing_estimation.py +347 -0
- edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
- edsl/jobs/jobs_runner_asyncio.py +282 -0
- edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/key_management/__init__.py +28 -0
- edsl/key_management/key_lookup.py +161 -0
- edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
- edsl/key_management/key_lookup_collection.py +82 -0
- edsl/key_management/models.py +218 -0
- edsl/language_models/__init__.py +7 -2
- edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
- edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
- edsl/language_models/language_model.py +1080 -0
- edsl/language_models/model.py +10 -25
- edsl/language_models/{ModelList.py → model_list.py} +9 -14
- edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
- edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
- edsl/language_models/repair.py +4 -4
- edsl/language_models/utilities.py +4 -4
- edsl/notebooks/__init__.py +3 -1
- edsl/notebooks/{Notebook.py → notebook.py} +7 -8
- edsl/prompts/__init__.py +1 -1
- edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
- edsl/prompts/{Prompt.py → prompt.py} +101 -95
- edsl/questions/HTMLQuestion.py +1 -1
- edsl/questions/__init__.py +154 -25
- edsl/questions/answer_validator_mixin.py +1 -1
- edsl/questions/compose_questions.py +4 -3
- edsl/questions/derived/question_likert_five.py +166 -0
- edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
- edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
- edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
- edsl/questions/descriptors.py +24 -30
- edsl/questions/loop_processor.py +65 -19
- edsl/questions/question_base.py +881 -0
- edsl/questions/question_base_gen_mixin.py +15 -16
- edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
- edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
- edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
- edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
- edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
- edsl/questions/question_free_text.py +282 -0
- edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
- edsl/questions/{QuestionList.py → question_list.py} +6 -7
- edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
- edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
- edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
- edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
- edsl/questions/question_registry.py +10 -16
- edsl/questions/register_questions_meta.py +8 -4
- edsl/questions/response_validator_abc.py +17 -16
- edsl/results/__init__.py +4 -1
- edsl/{exceptions/results.py → results/exceptions.py} +1 -1
- edsl/results/report.py +197 -0
- edsl/results/{Result.py → result.py} +131 -45
- edsl/results/{Results.py → results.py} +420 -216
- edsl/results/results_selector.py +344 -25
- edsl/scenarios/__init__.py +30 -3
- edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
- edsl/scenarios/directory_scanner.py +156 -13
- edsl/scenarios/document_chunker.py +186 -0
- edsl/scenarios/exceptions.py +101 -0
- edsl/scenarios/file_methods.py +2 -3
- edsl/scenarios/file_store.py +755 -0
- edsl/scenarios/handlers/__init__.py +14 -14
- edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
- edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
- edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
- edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
- edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
- edsl/scenarios/handlers/latex_file_store.py +5 -0
- edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
- edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
- edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
- edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
- edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
- edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
- edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
- edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
- edsl/scenarios/scenario.py +928 -0
- edsl/scenarios/scenario_join.py +18 -5
- edsl/scenarios/{ScenarioList.py → scenario_list.py} +424 -106
- edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
- edsl/scenarios/scenario_selector.py +5 -1
- edsl/study/ObjectEntry.py +2 -2
- edsl/study/SnapShot.py +5 -5
- edsl/study/Study.py +20 -21
- edsl/study/__init__.py +6 -4
- edsl/surveys/__init__.py +7 -4
- edsl/surveys/dag/__init__.py +2 -0
- edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
- edsl/surveys/{DAG.py → dag/dag.py} +13 -10
- edsl/surveys/descriptors.py +1 -1
- edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
- edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
- edsl/surveys/memory/__init__.py +3 -0
- edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
- edsl/surveys/rules/__init__.py +3 -0
- edsl/surveys/{Rule.py → rules/rule.py} +103 -43
- edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
- edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
- edsl/surveys/survey.py +1743 -0
- edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
- edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
- edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
- edsl/tasks/__init__.py +32 -0
- edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
- edsl/tasks/task_creators.py +135 -0
- edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
- edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
- edsl/tasks/task_status_log.py +85 -0
- edsl/tokens/__init__.py +2 -0
- edsl/tokens/interview_token_usage.py +53 -0
- edsl/utilities/PrettyList.py +1 -1
- edsl/utilities/SystemInfo.py +25 -22
- edsl/utilities/__init__.py +29 -21
- edsl/utilities/gcp_bucket/__init__.py +2 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
- edsl/utilities/interface.py +44 -536
- edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
- edsl/utilities/repair_functions.py +1 -1
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/METADATA +3 -2
- edsl-0.1.48.dist-info/RECORD +347 -0
- edsl/Base.py +0 -426
- edsl/BaseDiff.py +0 -260
- edsl/agents/InvigilatorBase.py +0 -260
- edsl/agents/PromptConstructor.py +0 -318
- edsl/auto/AutoStudy.py +0 -130
- edsl/auto/StageBase.py +0 -243
- edsl/auto/StageGenerateSurvey.py +0 -178
- edsl/auto/StageLabelQuestions.py +0 -125
- edsl/auto/StagePersona.py +0 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +0 -88
- edsl/auto/StagePersonaDimensionValues.py +0 -74
- edsl/auto/StagePersonaDimensions.py +0 -69
- edsl/auto/StageQuestions.py +0 -74
- edsl/auto/SurveyCreatorPipeline.py +0 -21
- edsl/auto/utilities.py +0 -218
- edsl/base/Base.py +0 -279
- edsl/coop/PriceFetcher.py +0 -54
- edsl/data/Cache.py +0 -580
- edsl/data/CacheEntry.py +0 -230
- edsl/data/SQLiteDict.py +0 -292
- edsl/data/__init__.py +0 -5
- edsl/data/orm.py +0 -10
- edsl/exceptions/cache.py +0 -5
- edsl/exceptions/coop.py +0 -14
- edsl/exceptions/data.py +0 -14
- edsl/exceptions/scenarios.py +0 -29
- edsl/jobs/Answers.py +0 -43
- edsl/jobs/JobsPrompts.py +0 -354
- edsl/jobs/buckets/BucketCollection.py +0 -134
- edsl/jobs/buckets/ModelBuckets.py +0 -65
- edsl/jobs/buckets/TokenBucket.py +0 -283
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/interviews/Interview.py +0 -395
- edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
- edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
- edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
- edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
- edsl/jobs/tasks/TaskCreators.py +0 -64
- edsl/jobs/tasks/TaskStatusLog.py +0 -23
- edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
- edsl/language_models/LanguageModel.py +0 -635
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/models.py +0 -137
- edsl/questions/QuestionBase.py +0 -539
- edsl/questions/QuestionFreeText.py +0 -130
- edsl/questions/derived/QuestionLikertFive.py +0 -76
- edsl/results/DatasetExportMixin.py +0 -911
- edsl/results/ResultsExportMixin.py +0 -45
- edsl/results/TextEditor.py +0 -50
- edsl/results/results_fetch_mixin.py +0 -33
- edsl/results/results_tools_mixin.py +0 -98
- edsl/scenarios/DocumentChunker.py +0 -104
- edsl/scenarios/FileStore.py +0 -564
- edsl/scenarios/Scenario.py +0 -548
- edsl/scenarios/ScenarioHtmlMixin.py +0 -65
- edsl/scenarios/ScenarioListExportMixin.py +0 -45
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/shared.py +0 -1
- edsl/surveys/Survey.py +0 -1306
- edsl/surveys/SurveyQualtricsImport.py +0 -284
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/tools/__init__.py +0 -1
- edsl/tools/clusters.py +0 -192
- edsl/tools/embeddings.py +0 -27
- edsl/tools/embeddings_plotting.py +0 -118
- edsl/tools/plotting.py +0 -112
- edsl/tools/summarize.py +0 -18
- edsl/utilities/data/Registry.py +0 -6
- edsl/utilities/data/__init__.py +0 -1
- edsl/utilities/data/scooter_results.json +0 -1
- edsl-0.1.46.dist-info/RECORD +0 -366
- /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
- /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
- /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
- /edsl/{results → dataset/display}/table_data_class.py +0 -0
- /edsl/{results → dataset/display}/table_display.css +0 -0
- /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
- /edsl/{results → dataset}/tree_explore.py +0 -0
- /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
- /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
- /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
- /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
- /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
- /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
- /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
- /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
- /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
- /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
- /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
- /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
- /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
- /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
- /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -4,9 +4,8 @@ import itertools
|
|
4
4
|
from typing import Optional, List, Callable, Type, TYPE_CHECKING, Union
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
7
|
-
from
|
8
|
-
from
|
9
|
-
|
7
|
+
from .question_base import QuestionBase
|
8
|
+
from ..scenarios import ScenarioList
|
10
9
|
|
11
10
|
class QuestionBaseGenMixin:
|
12
11
|
"""Mixin for QuestionBase.
|
@@ -30,7 +29,7 @@ class QuestionBaseGenMixin:
|
|
30
29
|
def option_permutations(self) -> list[QuestionBase]:
|
31
30
|
"""Return a list of questions with all possible permutations of the options.
|
32
31
|
|
33
|
-
>>> from edsl.questions
|
32
|
+
>>> from edsl.questions import QuestionMultipleChoice as Q
|
34
33
|
>>> len(Q.example().option_permutations())
|
35
34
|
24
|
36
35
|
"""
|
@@ -53,7 +52,7 @@ class QuestionBaseGenMixin:
|
|
53
52
|
|
54
53
|
If the question has no options, returns a copy of the original question.
|
55
54
|
|
56
|
-
>>> from edsl.questions
|
55
|
+
>>> from edsl.questions import QuestionMultipleChoice as Q
|
57
56
|
>>> q = Q.example()
|
58
57
|
>>> drawn = q.draw()
|
59
58
|
>>> len(drawn.question_options) == len(q.question_options)
|
@@ -78,8 +77,8 @@ class QuestionBaseGenMixin:
|
|
78
77
|
|
79
78
|
:param scenario_list: The list of scenarios to loop through.
|
80
79
|
|
81
|
-
>>> from edsl.questions
|
82
|
-
>>> from edsl.scenarios
|
80
|
+
>>> from edsl.questions import QuestionFreeText
|
81
|
+
>>> from edsl.scenarios import ScenarioList
|
83
82
|
>>> q = QuestionFreeText(question_text = "What are your thoughts on: {{ subject}}?", question_name = "base_{{subject}}")
|
84
83
|
>>> len(q.loop(ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])))
|
85
84
|
3
|
@@ -100,31 +99,31 @@ class QuestionBaseGenMixin:
|
|
100
99
|
Raises:
|
101
100
|
MaxTemplateNestingExceeded: If template nesting exceeds MAX_NESTING levels
|
102
101
|
|
103
|
-
>>> from edsl.questions
|
102
|
+
>>> from edsl.questions import QuestionFreeText
|
104
103
|
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite {{ thing }}?")
|
105
104
|
>>> q.render({"thing": "color"})
|
106
105
|
Question('free_text', question_name = \"""color\""", question_text = \"""What is your favorite color?\""")
|
107
106
|
|
108
|
-
>>> from edsl.questions
|
107
|
+
>>> from edsl.questions import QuestionMultipleChoice
|
109
108
|
>>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
|
110
|
-
>>> from edsl.scenarios
|
109
|
+
>>> from edsl.scenarios import Scenario
|
111
110
|
>>> q.render(Scenario({"thing": "color"})).data
|
112
111
|
{'question_name': 'color', 'question_text': 'What is your favorite color?', 'question_options': ['red', 'blue', 'green']}
|
113
112
|
|
114
|
-
>>> from edsl.questions
|
113
|
+
>>> from edsl.questions import QuestionMultipleChoice
|
115
114
|
>>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
|
116
115
|
>>> q.render({"thing": 1}).data
|
117
116
|
{'question_name': 'color', 'question_text': 'What is your favorite 1?', 'question_options': ['red', 'blue', 'green']}
|
118
117
|
|
119
118
|
|
120
|
-
>>> from edsl.questions
|
121
|
-
>>> from edsl.scenarios
|
119
|
+
>>> from edsl.questions import QuestionMultipleChoice
|
120
|
+
>>> from edsl.scenarios import Scenario
|
122
121
|
>>> q = QuestionMultipleChoice(question_name = "color", question_text = "What is your favorite {{ thing }}?", question_options = ["red", "blue", "green"])
|
123
122
|
>>> q.render(Scenario({"thing": "color of {{ object }}", "object":"water"})).data
|
124
123
|
{'question_name': 'color', 'question_text': 'What is your favorite color of water?', 'question_options': ['red', 'blue', 'green']}
|
125
124
|
|
126
125
|
|
127
|
-
>>> from edsl.questions
|
126
|
+
>>> from edsl.questions import QuestionFreeText
|
128
127
|
>>> q = QuestionFreeText(question_name = "infinite", question_text = "This has {{ a }}")
|
129
128
|
>>> q.render({"a": "{{ b }}", "b": "{{ a }}"}) # doctest: +IGNORE_EXCEPTION_DETAIL
|
130
129
|
Traceback (most recent call last):
|
@@ -132,7 +131,7 @@ class QuestionBaseGenMixin:
|
|
132
131
|
edsl.questions.question_base_gen_mixin.QuestionBaseGenMixin.MaxTemplateNestingExceeded:...
|
133
132
|
"""
|
134
133
|
from jinja2 import Environment, meta
|
135
|
-
from edsl.scenarios
|
134
|
+
from edsl.scenarios import Scenario
|
136
135
|
|
137
136
|
MAX_NESTING = 10 # Maximum allowed nesting levels
|
138
137
|
|
@@ -187,7 +186,7 @@ class QuestionBaseGenMixin:
|
|
187
186
|
def apply_function(
|
188
187
|
self, func: Callable, exclude_components: Optional[List[str]] = None
|
189
188
|
) -> QuestionBase:
|
190
|
-
from
|
189
|
+
from .question_base import QuestionBase
|
191
190
|
d = self._apply_function_dict(func, exclude_components)
|
192
191
|
return QuestionBase.from_dict(d)
|
193
192
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from importlib import resources
|
2
2
|
from typing import Optional
|
3
|
-
from edsl.exceptions.questions import QuestionAnswerValidationError
|
4
3
|
from functools import lru_cache
|
5
4
|
|
5
|
+
from .exceptions import QuestionAnswerValidationError
|
6
6
|
|
7
7
|
class TemplateManager:
|
8
8
|
_instance = None
|
@@ -277,7 +277,7 @@ class QuestionBasePromptsMixin:
|
|
277
277
|
|
278
278
|
:param model: The language model to use.
|
279
279
|
"""
|
280
|
-
from
|
280
|
+
from ..prompts import Prompt
|
281
281
|
|
282
282
|
if model in self.model_instructions:
|
283
283
|
return Prompt(text=self.model_instructions[model])
|
@@ -3,10 +3,9 @@ from typing import Any, Optional, Union, List
|
|
3
3
|
|
4
4
|
from pydantic import Field, BaseModel, validator
|
5
5
|
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
9
|
-
|
6
|
+
from .question_base import QuestionBase
|
7
|
+
from .descriptors import IntegerDescriptor, QuestionOptionsDescriptor
|
8
|
+
from .response_validator_abc import ResponseValidatorABC
|
10
9
|
|
11
10
|
class BudgetResponseValidator(ResponseValidatorABC):
|
12
11
|
valid_examples = []
|
@@ -1,25 +1,27 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import random
|
3
|
-
from typing import Any, Optional, Union
|
3
|
+
from typing import Any, Optional, Union, TYPE_CHECKING
|
4
4
|
|
5
5
|
from jinja2 import Template
|
6
|
+
from pydantic import field_validator
|
7
|
+
from pydantic import BaseModel, Field, conlist
|
8
|
+
from typing import List, Literal, Optional, Annotated
|
6
9
|
|
7
|
-
from
|
8
|
-
from
|
10
|
+
from .exceptions import QuestionAnswerValidationError
|
11
|
+
from ..scenarios import Scenario
|
12
|
+
|
13
|
+
from .question_base import QuestionBase
|
14
|
+
from .descriptors import (
|
9
15
|
IntegerDescriptor,
|
10
16
|
QuestionOptionsDescriptor,
|
11
17
|
)
|
18
|
+
from .decorators import inject_exception
|
19
|
+
from .response_validator_abc import ResponseValidatorABC
|
12
20
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
from edsl.questions.data_structures import BaseResponse
|
18
|
-
|
19
|
-
from edsl.exceptions.questions import QuestionAnswerValidationError
|
20
|
-
|
21
|
-
from pydantic import BaseModel, Field, conlist
|
22
|
-
from typing import List, Literal, Optional, Annotated
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from .data_structures import (
|
23
|
+
BaseResponse,
|
24
|
+
)
|
23
25
|
|
24
26
|
|
25
27
|
def create_checkbox_response_model(
|
@@ -164,7 +166,7 @@ class CheckBoxResponseValidator(ResponseValidatorABC):
|
|
164
166
|
print(f"Proposed solution {proposed_data} is invalid. Error: {e}")
|
165
167
|
return response
|
166
168
|
|
167
|
-
def custom_validate(self, response) -> BaseResponse:
|
169
|
+
def custom_validate(self, response) -> 'BaseResponse':
|
168
170
|
if response.answer is None:
|
169
171
|
raise QuestionAnswerValidationError("Answer is missing.")
|
170
172
|
return response.dict()
|
@@ -241,8 +243,6 @@ class QuestionCheckBox(QuestionBase):
|
|
241
243
|
For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
|
242
244
|
The LLM will respond with [0,1] and this code will translate it to ["a","b"].
|
243
245
|
"""
|
244
|
-
from edsl.scenarios.Scenario import Scenario
|
245
|
-
|
246
246
|
scenario = scenario or Scenario()
|
247
247
|
translated_options = [
|
248
248
|
Template(str(option)).render(scenario) for option in self.question_options
|
@@ -4,16 +4,16 @@ from pydantic import BaseModel, Field, field_validator
|
|
4
4
|
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
|
5
5
|
from pathlib import Path
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from .question_base import QuestionBase
|
8
|
+
from .descriptors import (
|
9
9
|
AnswerKeysDescriptor,
|
10
10
|
ValueTypesDescriptor,
|
11
11
|
ValueDescriptionsDescriptor,
|
12
12
|
QuestionTextDescriptor,
|
13
13
|
)
|
14
|
-
from
|
15
|
-
from
|
16
|
-
from
|
14
|
+
from .response_validator_abc import ResponseValidatorABC
|
15
|
+
from .exceptions import QuestionCreationValidationError
|
16
|
+
from .decorators import inject_exception
|
17
17
|
|
18
18
|
|
19
19
|
class DictResponseValidator(ResponseValidatorABC):
|
@@ -49,6 +49,40 @@ class DictResponseValidator(ResponseValidatorABC):
|
|
49
49
|
|
50
50
|
|
51
51
|
class QuestionDict(QuestionBase):
|
52
|
+
""" A QuestionDict allows you to create questions that expect dictionary responses with specific keys and value types.
|
53
|
+
|
54
|
+
Documenation: https://docs.expectedparrot.com/en/latest/questions.html#questiondict
|
55
|
+
|
56
|
+
Parameters
|
57
|
+
----------
|
58
|
+
question_name : str
|
59
|
+
Unique identifier for the question
|
60
|
+
question_text : str
|
61
|
+
The actual question text presented to users
|
62
|
+
answer_keys : List[str]
|
63
|
+
Keys that must be provided in the answer dictionary
|
64
|
+
value_types : Optional[List[Union[str, type]]]
|
65
|
+
Expected data types for each answer key
|
66
|
+
value_descriptions : Optional[List[str]]
|
67
|
+
Human-readable descriptions for each answer key
|
68
|
+
include_comment : bool
|
69
|
+
Whether to allow additional comments with the answer
|
70
|
+
question_presentation : Optional[str]
|
71
|
+
Alternative way to present the question
|
72
|
+
answering_instructions : Optional[str]
|
73
|
+
Additional instructions for answering
|
74
|
+
permissive : bool
|
75
|
+
If True, allows additional keys not specified in answer_keys
|
76
|
+
|
77
|
+
Examples
|
78
|
+
--------
|
79
|
+
>>> q = QuestionDict(
|
80
|
+
... question_name="tweet",
|
81
|
+
... question_text="Draft a tweet.",
|
82
|
+
... answer_keys=["text", "characters"],
|
83
|
+
... value_descriptions=["The text of the tweet", "The number of characters in the tweet"]
|
84
|
+
... )
|
85
|
+
"""
|
52
86
|
question_type = "dict"
|
53
87
|
question_text: str = QuestionTextDescriptor()
|
54
88
|
answer_keys: List[str] = AnswerKeysDescriptor()
|
@@ -1,18 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import json
|
3
3
|
import re
|
4
|
-
|
4
|
+
from typing import Dict, Any
|
5
5
|
from typing import Any, Optional, Dict
|
6
|
-
from edsl.questions.QuestionBase import QuestionBase
|
7
|
-
from edsl.questions.descriptors import AnswerTemplateDescriptor
|
8
|
-
|
9
|
-
from edsl.questions.response_validator_abc import ResponseValidatorABC
|
10
|
-
from edsl.questions.data_structures import BaseResponse
|
11
|
-
from edsl.questions.decorators import inject_exception
|
12
6
|
|
13
|
-
from typing import Dict, Any
|
14
7
|
from pydantic import create_model, Field
|
15
8
|
|
9
|
+
from .question_base import QuestionBase
|
10
|
+
from .descriptors import AnswerTemplateDescriptor
|
11
|
+
|
12
|
+
from .response_validator_abc import ResponseValidatorABC
|
13
|
+
from .data_structures import BaseResponse
|
14
|
+
from .decorators import inject_exception
|
15
|
+
|
16
16
|
|
17
17
|
def extract_json(text, expected_keys, verbose=False):
|
18
18
|
# Escape special regex characters in keys
|
@@ -156,7 +156,7 @@ class QuestionExtract(QuestionBase):
|
|
156
156
|
|
157
157
|
def main():
|
158
158
|
"""Administer a question and validate the answer."""
|
159
|
-
from edsl.questions
|
159
|
+
from edsl.questions import QuestionExtract
|
160
160
|
|
161
161
|
q = QuestionExtract.example()
|
162
162
|
q.question_text
|
@@ -0,0 +1,282 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, Optional
|
3
|
+
from typing import Optional, Any, List
|
4
|
+
|
5
|
+
from uuid import uuid4
|
6
|
+
|
7
|
+
from pydantic import field_validator, model_validator, BaseModel
|
8
|
+
|
9
|
+
from ..prompts import Prompt
|
10
|
+
|
11
|
+
from .exceptions import QuestionAnswerValidationError
|
12
|
+
from .question_base import QuestionBase
|
13
|
+
from .response_validator_abc import ResponseValidatorABC
|
14
|
+
from .decorators import inject_exception
|
15
|
+
|
16
|
+
|
17
|
+
class FreeTextResponse(BaseModel):
|
18
|
+
"""
|
19
|
+
Pydantic model for validating free text responses.
|
20
|
+
|
21
|
+
This model defines the structure and validation rules for responses to
|
22
|
+
free text questions. It ensures that responses contain a valid text string
|
23
|
+
and that the answer and generated_tokens fields match when both are present.
|
24
|
+
|
25
|
+
Attributes:
|
26
|
+
answer: The text response string.
|
27
|
+
generated_tokens: Optional raw LLM output for token tracking.
|
28
|
+
"""
|
29
|
+
|
30
|
+
answer: str
|
31
|
+
generated_tokens: Optional[str] = None
|
32
|
+
|
33
|
+
@model_validator(mode='after')
|
34
|
+
def validate_tokens_match_answer(self):
|
35
|
+
"""
|
36
|
+
Validate that the answer matches the generated tokens if provided.
|
37
|
+
|
38
|
+
This validator ensures consistency between the answer and generated_tokens
|
39
|
+
fields when both are present. They must match exactly (after stripping
|
40
|
+
whitespace) to ensure token tracking accuracy.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
The validated model instance.
|
44
|
+
|
45
|
+
Raises:
|
46
|
+
ValueError: If the answer and generated_tokens don't match exactly.
|
47
|
+
"""
|
48
|
+
if self.generated_tokens is not None: # If generated_tokens exists
|
49
|
+
# Ensure exact string equality
|
50
|
+
if self.answer.strip() != self.generated_tokens.strip(): # They MUST match exactly
|
51
|
+
raise ValueError(
|
52
|
+
f"answer '{self.answer}' must exactly match generated_tokens '{self.generated_tokens}'. "
|
53
|
+
f"Type of answer: {type(self.answer)}, Type of tokens: {type(self.generated_tokens)}"
|
54
|
+
)
|
55
|
+
return self
|
56
|
+
|
57
|
+
|
58
|
+
class FreeTextResponseValidator(ResponseValidatorABC):
|
59
|
+
"""
|
60
|
+
Validator for free text question responses.
|
61
|
+
|
62
|
+
This class implements the validation and fixing logic for free text responses.
|
63
|
+
It ensures that responses contain a valid text string and provides methods
|
64
|
+
to fix common issues in responses.
|
65
|
+
|
66
|
+
Attributes:
|
67
|
+
required_params: List of required parameters for validation.
|
68
|
+
valid_examples: Examples of valid responses for testing.
|
69
|
+
invalid_examples: Examples of invalid responses for testing.
|
70
|
+
"""
|
71
|
+
required_params = []
|
72
|
+
valid_examples = [({"answer": "This is great"}, {})]
|
73
|
+
invalid_examples = [
|
74
|
+
(
|
75
|
+
{"answer": None},
|
76
|
+
{},
|
77
|
+
"Answer code must not be missing.",
|
78
|
+
),
|
79
|
+
]
|
80
|
+
|
81
|
+
def fix(self, response: dict, verbose: bool = False) -> dict:
|
82
|
+
"""
|
83
|
+
Fix common issues in free text responses.
|
84
|
+
|
85
|
+
This method attempts to fix invalid responses by ensuring the answer
|
86
|
+
field contains a valid string and is consistent with the generated_tokens
|
87
|
+
field if present.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
response: The response dictionary to fix.
|
91
|
+
verbose: If True, print information about the fixing process.
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
A fixed version of the response dictionary.
|
95
|
+
|
96
|
+
Notes:
|
97
|
+
- For free text responses, the answer is always synchronized with generated_tokens
|
98
|
+
- Both fields are converted to strings to ensure type consistency
|
99
|
+
"""
|
100
|
+
if response.get("generated_tokens") != response.get("answer"):
|
101
|
+
return {
|
102
|
+
"answer": str(response.get("generated_tokens")),
|
103
|
+
"generated_tokens": str(response.get("generated_tokens")),
|
104
|
+
}
|
105
|
+
else:
|
106
|
+
return {
|
107
|
+
"answer": str(response.get("generated_tokens")),
|
108
|
+
"generated_tokens": str(response.get("generated_tokens")),
|
109
|
+
}
|
110
|
+
|
111
|
+
|
112
|
+
class QuestionFreeText(QuestionBase):
|
113
|
+
"""
|
114
|
+
A question that allows an agent to respond with free-form text.
|
115
|
+
|
116
|
+
QuestionFreeText is one of the simplest and most commonly used question types
|
117
|
+
in EDSL. It prompts an agent or language model to provide a textual response
|
118
|
+
without any specific structure or constraints on the format. The response can
|
119
|
+
be of any length and content, making it suitable for open-ended questions,
|
120
|
+
explanations, storytelling, and other scenarios requiring unrestricted text.
|
121
|
+
|
122
|
+
Attributes:
|
123
|
+
question_type (str): Identifier for this question type, set to "free_text".
|
124
|
+
_response_model: Pydantic model for validating responses.
|
125
|
+
response_validator_class: Class used to validate and fix responses.
|
126
|
+
|
127
|
+
Examples:
|
128
|
+
>>> q = QuestionFreeText(
|
129
|
+
... question_name="opinion",
|
130
|
+
... question_text="What do you think about AI?"
|
131
|
+
... )
|
132
|
+
>>> q.question_type
|
133
|
+
'free_text'
|
134
|
+
|
135
|
+
>>> from edsl.language_models import Model
|
136
|
+
>>> model = Model("test", canned_response="I think AI is fascinating.")
|
137
|
+
>>> result = q.by(model).run(disable_remote_inference=True)
|
138
|
+
>>> answer = result.select("answer.*").to_list()[0]
|
139
|
+
>>> "fascinating" in answer
|
140
|
+
True
|
141
|
+
"""
|
142
|
+
|
143
|
+
question_type = "free_text"
|
144
|
+
_response_model = FreeTextResponse
|
145
|
+
response_validator_class = FreeTextResponseValidator
|
146
|
+
|
147
|
+
def __init__(
|
148
|
+
self,
|
149
|
+
question_name: str,
|
150
|
+
question_text: str,
|
151
|
+
answering_instructions: Optional[str] = None,
|
152
|
+
question_presentation: Optional[str] = None,
|
153
|
+
):
|
154
|
+
"""
|
155
|
+
Initialize a new free text question.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
question_name: Identifier for the question, used in results and templates.
|
159
|
+
Must be a valid Python variable name.
|
160
|
+
question_text: The actual text of the question to be asked.
|
161
|
+
answering_instructions: Optional additional instructions for answering
|
162
|
+
the question, overrides default instructions.
|
163
|
+
question_presentation: Optional custom presentation template for the
|
164
|
+
question, overrides default presentation.
|
165
|
+
|
166
|
+
Examples:
|
167
|
+
>>> q = QuestionFreeText(
|
168
|
+
... question_name="feedback",
|
169
|
+
... question_text="Please provide your thoughts on this product."
|
170
|
+
... )
|
171
|
+
>>> q.question_name
|
172
|
+
'feedback'
|
173
|
+
|
174
|
+
>>> q = QuestionFreeText(
|
175
|
+
... question_name="explanation",
|
176
|
+
... question_text="Explain how photosynthesis works.",
|
177
|
+
... answering_instructions="Provide a detailed scientific explanation."
|
178
|
+
... )
|
179
|
+
"""
|
180
|
+
self.question_name = question_name
|
181
|
+
self.question_text = question_text
|
182
|
+
self.answering_instructions = answering_instructions
|
183
|
+
self.question_presentation = question_presentation
|
184
|
+
|
185
|
+
@property
|
186
|
+
def question_html_content(self) -> str:
|
187
|
+
"""
|
188
|
+
Generate HTML content for rendering the question in web interfaces.
|
189
|
+
|
190
|
+
This property generates HTML markup for the question when it needs to be
|
191
|
+
displayed in web interfaces or HTML contexts. For a free text question,
|
192
|
+
this is typically a textarea element.
|
193
|
+
|
194
|
+
Returns:
|
195
|
+
str: HTML markup for rendering the question.
|
196
|
+
|
197
|
+
Notes:
|
198
|
+
- Uses Jinja2 templating to generate the HTML
|
199
|
+
- Creates a textarea input element with the question_name as the ID and name
|
200
|
+
- Can be used for displaying the question in web UIs or HTML exports
|
201
|
+
"""
|
202
|
+
from jinja2 import Template
|
203
|
+
|
204
|
+
question_html_content = Template(
|
205
|
+
"""
|
206
|
+
<div>
|
207
|
+
<textarea id="{{ question_name }}" name="{{ question_name }}"></textarea>
|
208
|
+
</div>
|
209
|
+
"""
|
210
|
+
).render(question_name=self.question_name)
|
211
|
+
return question_html_content
|
212
|
+
|
213
|
+
@classmethod
|
214
|
+
@inject_exception
|
215
|
+
def example(cls, randomize: bool = False) -> "QuestionFreeText":
|
216
|
+
"""
|
217
|
+
Create an example instance of a free text question.
|
218
|
+
|
219
|
+
This class method creates a predefined example of a free text question
|
220
|
+
for demonstration, testing, and documentation purposes.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
randomize: If True, appends a random UUID to the question text to
|
224
|
+
ensure uniqueness in tests and examples.
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
QuestionFreeText: An example free text question.
|
228
|
+
|
229
|
+
Examples:
|
230
|
+
>>> q = QuestionFreeText.example()
|
231
|
+
>>> q.question_name
|
232
|
+
'how_are_you'
|
233
|
+
>>> q.question_text
|
234
|
+
'How are you?'
|
235
|
+
|
236
|
+
>>> q1 = QuestionFreeText.example(randomize=True)
|
237
|
+
>>> q2 = QuestionFreeText.example(randomize=True)
|
238
|
+
>>> q1.question_text != q2.question_text
|
239
|
+
True
|
240
|
+
"""
|
241
|
+
addition = "" if not randomize else str(uuid4())
|
242
|
+
return cls(question_name="how_are_you", question_text=f"How are you?{addition}")
|
243
|
+
|
244
|
+
|
245
|
+
def main():
|
246
|
+
"""
|
247
|
+
Demonstrate the functionality of the QuestionFreeText class.
|
248
|
+
|
249
|
+
This function creates an example free text question and demonstrates its
|
250
|
+
key features including validation, serialization, and answer simulation.
|
251
|
+
It's primarily intended for testing and development purposes.
|
252
|
+
|
253
|
+
Note:
|
254
|
+
This function will be executed when the module is run directly,
|
255
|
+
but not when imported.
|
256
|
+
"""
|
257
|
+
from .question_free_text import QuestionFreeText
|
258
|
+
|
259
|
+
# Create an example question
|
260
|
+
q = QuestionFreeText.example()
|
261
|
+
print(f"Question text: {q.question_text}")
|
262
|
+
print(f"Question name: {q.question_name}")
|
263
|
+
|
264
|
+
# Validate an answer
|
265
|
+
valid_answer = {"answer": "I like custard", "generated_tokens": "I like custard"}
|
266
|
+
validated = q._validate_answer(valid_answer)
|
267
|
+
print(f"Validated answer: {validated}")
|
268
|
+
|
269
|
+
# Simulate an answer
|
270
|
+
simulated = q._simulate_answer()
|
271
|
+
print(f"Simulated answer: {simulated}")
|
272
|
+
|
273
|
+
# Serialization demonstration
|
274
|
+
serialized = q.to_dict()
|
275
|
+
print(f"Serialized: {serialized}")
|
276
|
+
deserialized = QuestionBase.from_dict(serialized)
|
277
|
+
print(f"Deserialization successful: {deserialized.question_text == q.question_text}")
|
278
|
+
|
279
|
+
# Run doctests
|
280
|
+
import doctest
|
281
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
282
|
+
print("Doctests completed")
|
@@ -1,10 +1,11 @@
|
|
1
|
+
from __future__ import annotations
|
1
2
|
from typing import Optional, Callable
|
2
3
|
import inspect
|
3
4
|
|
4
|
-
from
|
5
|
+
from .question_base import QuestionBase
|
5
6
|
|
6
|
-
from
|
7
|
-
from
|
7
|
+
from ..utilities.restricted_python import create_restricted_function
|
8
|
+
from ..utilities.decorators import add_edsl_version, remove_edsl_version
|
8
9
|
|
9
10
|
|
10
11
|
class QuestionFunctional(QuestionBase):
|
@@ -25,7 +26,7 @@ class QuestionFunctional(QuestionBase):
|
|
25
26
|
|
26
27
|
# Serialize the question to a dictionary
|
27
28
|
|
28
|
-
>>> from
|
29
|
+
>>> from .question_base import QuestionBase
|
29
30
|
>>> new_question = QuestionBase.from_dict(question.to_dict())
|
30
31
|
>>> results = new_question.by(scenario).by(agent).run(disable_remote_cache = True, disable_remote_inference = True)
|
31
32
|
>>> results.select("answer.*").to_list()[0] == 150
|
@@ -146,7 +147,7 @@ def calculate_sum_and_multiply(scenario, agent_traits):
|
|
146
147
|
|
147
148
|
def main():
|
148
149
|
from edsl import Scenario, Agent
|
149
|
-
from
|
150
|
+
from .question_functional import QuestionFunctional
|
150
151
|
|
151
152
|
# Create an instance of QuestionFunctional with the new function
|
152
153
|
question = QuestionFunctional.example()
|
@@ -5,12 +5,11 @@ from typing import Any, Optional, Union
|
|
5
5
|
from pydantic import Field
|
6
6
|
from json_repair import repair_json
|
7
7
|
|
8
|
-
from
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from
|
13
|
-
|
8
|
+
from .exceptions import QuestionAnswerValidationError
|
9
|
+
from .question_base import QuestionBase
|
10
|
+
from .descriptors import IntegerOrNoneDescriptor
|
11
|
+
from .decorators import inject_exception
|
12
|
+
from .response_validator_abc import ResponseValidatorABC
|
14
13
|
|
15
14
|
def convert_string(s: str) -> Union[float, int, str, dict]:
|
16
15
|
"""Convert a string to a more appropriate type if possible.
|
@@ -198,7 +197,7 @@ class QuestionList(QuestionBase):
|
|
198
197
|
|
199
198
|
def main():
|
200
199
|
"""Create an example of a list question and demonstrate its functionality."""
|
201
|
-
from edsl.questions
|
200
|
+
from edsl.questions import QuestionList
|
202
201
|
|
203
202
|
q = QuestionList.example(max_list_items=5)
|
204
203
|
q.question_text
|
@@ -4,15 +4,16 @@ from typing import Union, Optional, Dict, List, Any
|
|
4
4
|
from pydantic import BaseModel, Field, field_validator
|
5
5
|
from jinja2 import Template
|
6
6
|
import random
|
7
|
-
from
|
8
|
-
from
|
7
|
+
from .question_base import QuestionBase
|
8
|
+
from .descriptors import (
|
9
9
|
QuestionOptionsDescriptor,
|
10
10
|
OptionLabelDescriptor,
|
11
11
|
QuestionTextDescriptor,
|
12
12
|
)
|
13
|
-
from
|
14
|
-
from
|
15
|
-
|
13
|
+
from .response_validator_abc import ResponseValidatorABC
|
14
|
+
from .decorators import inject_exception
|
15
|
+
|
16
|
+
from .exceptions import (
|
16
17
|
QuestionAnswerValidationError,
|
17
18
|
QuestionCreationValidationError,
|
18
19
|
)
|