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
@@ -0,0 +1,137 @@
|
|
1
|
+
from jinja2 import Environment, meta
|
2
|
+
from typing import Any, Set, TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from edsl.agents.PromptConstructor import PromptConstructor
|
6
|
+
from edsl.scenarios.Scenario import Scenario
|
7
|
+
|
8
|
+
|
9
|
+
class QuestionTemplateReplacementsBuilder:
|
10
|
+
def __init__(self, prompt_constructor: "PromptConstructor"):
|
11
|
+
self.prompt_constructor = prompt_constructor
|
12
|
+
|
13
|
+
def question_file_keys(self):
|
14
|
+
question_text = self.prompt_constructor.question.question_text
|
15
|
+
file_keys = self._find_file_keys(self.prompt_constructor.scenario)
|
16
|
+
return self._extract_file_keys_from_question_text(question_text, file_keys)
|
17
|
+
|
18
|
+
def scenario_file_keys(self):
|
19
|
+
return self._find_file_keys(self.prompt_constructor.scenario)
|
20
|
+
|
21
|
+
def get_jinja2_variables(template_str: str) -> Set[str]:
|
22
|
+
"""
|
23
|
+
Extracts all variable names from a Jinja2 template using Jinja2's built-in parsing.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
template_str (str): The Jinja2 template string
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
Set[str]: A set of variable names found in the template
|
30
|
+
"""
|
31
|
+
env = Environment()
|
32
|
+
ast = env.parse(template_str)
|
33
|
+
return meta.find_undeclared_variables(ast)
|
34
|
+
|
35
|
+
@staticmethod
|
36
|
+
def _find_file_keys(scenario: "Scenario") -> list:
|
37
|
+
"""We need to find all the keys in the scenario that refer to FileStore objects.
|
38
|
+
These will be used to append to the prompt a list of files that are part of the scenario.
|
39
|
+
|
40
|
+
>>> from edsl import Scenario
|
41
|
+
>>> from edsl.scenarios.FileStore import FileStore
|
42
|
+
>>> import tempfile
|
43
|
+
>>> with tempfile.NamedTemporaryFile() as f:
|
44
|
+
... _ = f.write(b"Hello, world!")
|
45
|
+
... _ = f.seek(0)
|
46
|
+
... fs = FileStore(f.name)
|
47
|
+
... scenario = Scenario({"fs_file": fs, 'a': 1})
|
48
|
+
... QuestionTemplateReplacementsBuilder._find_file_keys(scenario)
|
49
|
+
['fs_file']
|
50
|
+
"""
|
51
|
+
from edsl.scenarios.FileStore import FileStore
|
52
|
+
|
53
|
+
file_entries = []
|
54
|
+
for key, value in scenario.items():
|
55
|
+
if isinstance(value, FileStore):
|
56
|
+
file_entries.append(key)
|
57
|
+
return file_entries
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def _extract_file_keys_from_question_text(
|
61
|
+
question_text: str, scenario_file_keys: list
|
62
|
+
) -> list:
|
63
|
+
"""
|
64
|
+
Extracts the file keys from a question text.
|
65
|
+
|
66
|
+
>>> from edsl import Scenario
|
67
|
+
>>> from edsl.scenarios.FileStore import FileStore
|
68
|
+
>>> import tempfile
|
69
|
+
>>> with tempfile.NamedTemporaryFile() as f:
|
70
|
+
... _ = f.write(b"Hello, world!")
|
71
|
+
... _ = f.seek(0)
|
72
|
+
... fs = FileStore(f.name)
|
73
|
+
... scenario = Scenario({"fs_file": fs, 'a': 1})
|
74
|
+
... QuestionTemplateReplacementsBuilder._extract_file_keys_from_question_text("{{ fs_file }}", ['fs_file'])
|
75
|
+
['fs_file']
|
76
|
+
"""
|
77
|
+
variables = QuestionTemplateReplacementsBuilder.get_jinja2_variables(
|
78
|
+
question_text
|
79
|
+
)
|
80
|
+
question_file_keys = []
|
81
|
+
for var in variables:
|
82
|
+
if var in scenario_file_keys:
|
83
|
+
question_file_keys.append(var)
|
84
|
+
return question_file_keys
|
85
|
+
|
86
|
+
def _scenario_replacements(self) -> dict[str, Any]:
|
87
|
+
# File references dictionary
|
88
|
+
file_refs = {key: f"<see file {key}>" for key in self.scenario_file_keys()}
|
89
|
+
|
90
|
+
# Scenario items excluding file keys
|
91
|
+
scenario_items = {
|
92
|
+
k: v
|
93
|
+
for k, v in self.prompt_constructor.scenario.items()
|
94
|
+
if k not in self.scenario_file_keys()
|
95
|
+
}
|
96
|
+
return {**file_refs, **scenario_items}
|
97
|
+
|
98
|
+
@staticmethod
|
99
|
+
def _question_data_replacements(
|
100
|
+
question: dict, question_data: dict
|
101
|
+
) -> dict[str, Any]:
|
102
|
+
"""Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources.
|
103
|
+
|
104
|
+
>>> from edsl import QuestionMultipleChoice
|
105
|
+
>>> q = QuestionMultipleChoice(question_text="Do you like school?", question_name = "q0", question_options = ["yes", "no"])
|
106
|
+
>>> QuestionTemplateReplacementsBuilder._question_data_replacements(q, q.data)
|
107
|
+
{'use_code': False, 'include_comment': True, 'question_name': 'q0', 'question_text': 'Do you like school?', 'question_options': ['yes', 'no']}
|
108
|
+
|
109
|
+
"""
|
110
|
+
question_settings = {
|
111
|
+
"use_code": getattr(question, "_use_code", True),
|
112
|
+
"include_comment": getattr(question, "_include_comment", False),
|
113
|
+
}
|
114
|
+
return {**question_settings, **question_data}
|
115
|
+
|
116
|
+
def build_replacement_dict(self, question_data: dict) -> dict[str, Any]:
|
117
|
+
"""Builds a dictionary of replacement values for rendering a prompt by combining multiple data sources."""
|
118
|
+
rpl = {}
|
119
|
+
rpl["scenario"] = self._scenario_replacements()
|
120
|
+
rpl["question"] = self._question_data_replacements(
|
121
|
+
self.prompt_constructor.question, question_data
|
122
|
+
)
|
123
|
+
rpl["prior_answers"] = self.prompt_constructor.prior_answers_dict()
|
124
|
+
rpl["agent"] = {"agent": self.prompt_constructor.agent}
|
125
|
+
|
126
|
+
# Combine all dictionaries using dict.update() for clarity
|
127
|
+
replacement_dict = {}
|
128
|
+
for r in rpl.values():
|
129
|
+
replacement_dict.update(r)
|
130
|
+
|
131
|
+
return replacement_dict
|
132
|
+
|
133
|
+
|
134
|
+
if __name__ == "__main__":
|
135
|
+
import doctest
|
136
|
+
|
137
|
+
doctest.testmod()
|
edsl/agents/descriptors.py
CHANGED
@@ -1,42 +1,68 @@
|
|
1
|
+
"""This module contains the descriptors used to set the attributes of the Agent class."""
|
2
|
+
|
1
3
|
from typing import Dict
|
2
|
-
from edsl.
|
3
|
-
|
4
|
+
from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
|
5
|
+
|
6
|
+
|
7
|
+
def convert_agent_name(x):
|
8
|
+
# potentially a numpy int64
|
9
|
+
import numpy as np
|
10
|
+
|
11
|
+
if isinstance(x, np.int64):
|
12
|
+
return int(x)
|
13
|
+
elif x is None:
|
14
|
+
return None
|
15
|
+
elif isinstance(x, int):
|
16
|
+
return x
|
17
|
+
else:
|
18
|
+
return str(x)
|
4
19
|
|
5
20
|
|
6
21
|
class NameDescriptor:
|
7
22
|
"""ABC for something."""
|
8
23
|
|
9
24
|
def __get__(self, instance, owner):
|
10
|
-
""""""
|
25
|
+
"""Return the value of the attribute."""
|
11
26
|
return instance.__dict__[self.name]
|
12
27
|
|
13
28
|
def __set__(self, instance, name: str) -> None:
|
14
|
-
|
29
|
+
"""Set the value of the attribute."""
|
30
|
+
instance.__dict__[self.name] = convert_agent_name(name)
|
15
31
|
|
16
32
|
def __set_name__(self, owner, name: str) -> None:
|
33
|
+
"""Set the name of the attribute."""
|
17
34
|
self.name = "_" + name
|
18
35
|
|
19
36
|
|
20
37
|
class TraitsDescriptor:
|
21
|
-
"""
|
38
|
+
"""Traits descriptor."""
|
22
39
|
|
23
40
|
def __get__(self, instance, owner):
|
24
|
-
""""""
|
41
|
+
"""Return the value of the attribute."""
|
25
42
|
return instance.__dict__[self.name]
|
26
43
|
|
27
44
|
def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
|
45
|
+
"""Set the value of the attribute."""
|
46
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
47
|
+
|
28
48
|
for key, value in traits_dict.items():
|
29
|
-
if not is_valid_variable_name(key):
|
30
|
-
raise AgentNameError("Trait keys must be a valid variable name!")
|
31
49
|
if key == "name":
|
32
50
|
raise AgentNameError(
|
33
|
-
"
|
34
|
-
|
35
|
-
"""
|
51
|
+
"Trait keys cannot be 'name'. Instead, use the 'name' attribute directly e.g.,\n"
|
52
|
+
'Agent(name="my_agent", traits={"trait1": "value1", "trait2": "value2"})'
|
36
53
|
)
|
54
|
+
|
55
|
+
if not is_valid_variable_name(key):
|
56
|
+
raise AgentTraitKeyError(
|
57
|
+
f"""Trait keys must be valid Python identifiers (must be alphanumeric, cannot start with a number and must use underscores instead of spaces).
|
58
|
+
You passed: {key}
|
59
|
+
"""
|
60
|
+
)
|
61
|
+
|
37
62
|
instance.__dict__[self.name] = traits_dict
|
38
63
|
|
39
64
|
def __set_name__(self, owner, name: str) -> None:
|
65
|
+
"""Set the name of the attribute."""
|
40
66
|
self.name = name
|
41
67
|
|
42
68
|
|
@@ -44,13 +70,15 @@ class CodebookDescriptor:
|
|
44
70
|
"""ABC for something."""
|
45
71
|
|
46
72
|
def __get__(self, instance, owner):
|
47
|
-
""""""
|
73
|
+
"""Return the value of the attribute."""
|
48
74
|
return instance.__dict__[self.name]
|
49
75
|
|
50
76
|
def __set__(self, instance, codebook_dict: Dict[str, str]) -> None:
|
77
|
+
"""Set the value of the attribute."""
|
51
78
|
instance.__dict__[self.name] = codebook_dict
|
52
79
|
|
53
80
|
def __set_name__(self, owner, name: str) -> None:
|
81
|
+
"""Set the name of the attribute."""
|
54
82
|
self.name = "_" + name
|
55
83
|
|
56
84
|
|
@@ -58,12 +86,14 @@ class InstructionDescriptor:
|
|
58
86
|
"""ABC for something."""
|
59
87
|
|
60
88
|
def __get__(self, instance, owner):
|
61
|
-
""""""
|
89
|
+
"""Return the value of the attribute."""
|
62
90
|
return instance.__dict__[self.name]
|
63
91
|
|
64
92
|
def __set__(self, instance, instruction) -> None:
|
93
|
+
"""Set the value of the attribute."""
|
65
94
|
instance.__dict__[self.name] = instruction
|
66
95
|
instance.set_instructions = instruction != instance.default_instruction
|
67
96
|
|
68
97
|
def __set_name__(self, owner, name: str) -> None:
|
98
|
+
"""Set the name of the attribute."""
|
69
99
|
self.name = "_" + name
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import enum
|
2
|
+
from typing import Dict, Optional
|
3
|
+
from collections import UserList
|
4
|
+
from edsl.prompts.Prompt import Prompt
|
5
|
+
|
6
|
+
|
7
|
+
class PromptComponent(enum.Enum):
|
8
|
+
AGENT_INSTRUCTIONS = "agent_instructions"
|
9
|
+
AGENT_PERSONA = "agent_persona"
|
10
|
+
QUESTION_INSTRUCTIONS = "question_instructions"
|
11
|
+
PRIOR_QUESTION_MEMORY = "prior_question_memory"
|
12
|
+
|
13
|
+
|
14
|
+
class PromptList(UserList):
|
15
|
+
separator = Prompt("")
|
16
|
+
|
17
|
+
def reduce(self):
|
18
|
+
"""Reduce the list of prompts to a single prompt.
|
19
|
+
|
20
|
+
>>> p = PromptList([Prompt("You are a happy-go lucky agent."), Prompt("You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}")])
|
21
|
+
>>> p.reduce()
|
22
|
+
Prompt(text=\"""You are a happy-go lucky agent.You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}\""")
|
23
|
+
|
24
|
+
"""
|
25
|
+
p = self[0]
|
26
|
+
for prompt in self[1:]:
|
27
|
+
if len(prompt) > 0:
|
28
|
+
p = p + self.separator + prompt
|
29
|
+
return p
|
30
|
+
|
31
|
+
|
32
|
+
class PromptPlan:
|
33
|
+
"""A plan for constructing prompts for the LLM call.
|
34
|
+
Every prompt plan has a user prompt order and a system prompt order.
|
35
|
+
It must contain each of the values in the PromptComponent enum.
|
36
|
+
|
37
|
+
|
38
|
+
>>> p = PromptPlan(user_prompt_order=(PromptComponent.AGENT_INSTRUCTIONS, PromptComponent.AGENT_PERSONA),system_prompt_order=(PromptComponent.QUESTION_INSTRUCTIONS, PromptComponent.PRIOR_QUESTION_MEMORY))
|
39
|
+
>>> p._is_valid_plan()
|
40
|
+
True
|
41
|
+
|
42
|
+
>>> p.arrange_components(agent_instructions=1, agent_persona=2, question_instructions=3, prior_question_memory=4)
|
43
|
+
{'user_prompt': ..., 'system_prompt': ...}
|
44
|
+
|
45
|
+
>>> p = PromptPlan(user_prompt_order=("agent_instructions", ), system_prompt_order=("question_instructions", "prior_question_memory"))
|
46
|
+
Traceback (most recent call last):
|
47
|
+
...
|
48
|
+
ValueError: Invalid plan: must contain each value of PromptComponent exactly once.
|
49
|
+
|
50
|
+
"""
|
51
|
+
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
user_prompt_order: Optional[tuple] = None,
|
55
|
+
system_prompt_order: Optional[tuple] = None,
|
56
|
+
):
|
57
|
+
"""Initialize the PromptPlan."""
|
58
|
+
|
59
|
+
if user_prompt_order is None:
|
60
|
+
user_prompt_order = (
|
61
|
+
PromptComponent.QUESTION_INSTRUCTIONS,
|
62
|
+
PromptComponent.PRIOR_QUESTION_MEMORY,
|
63
|
+
)
|
64
|
+
if system_prompt_order is None:
|
65
|
+
system_prompt_order = (
|
66
|
+
PromptComponent.AGENT_INSTRUCTIONS,
|
67
|
+
PromptComponent.AGENT_PERSONA,
|
68
|
+
)
|
69
|
+
|
70
|
+
# very commmon way to screw this up given how python treats single strings as iterables
|
71
|
+
if isinstance(user_prompt_order, str):
|
72
|
+
user_prompt_order = (user_prompt_order,)
|
73
|
+
|
74
|
+
if isinstance(system_prompt_order, str):
|
75
|
+
system_prompt_order = (system_prompt_order,)
|
76
|
+
|
77
|
+
if not isinstance(user_prompt_order, tuple):
|
78
|
+
raise TypeError(
|
79
|
+
f"Expected a tuple, but got {type(user_prompt_order).__name__}"
|
80
|
+
)
|
81
|
+
|
82
|
+
if not isinstance(system_prompt_order, tuple):
|
83
|
+
raise TypeError(
|
84
|
+
f"Expected a tuple, but got {type(system_prompt_order).__name__}"
|
85
|
+
)
|
86
|
+
|
87
|
+
self.user_prompt_order = self._convert_to_enum(user_prompt_order)
|
88
|
+
self.system_prompt_order = self._convert_to_enum(system_prompt_order)
|
89
|
+
if not self._is_valid_plan():
|
90
|
+
raise ValueError(
|
91
|
+
"Invalid plan: must contain each value of PromptComponent exactly once."
|
92
|
+
)
|
93
|
+
|
94
|
+
def _convert_to_enum(self, prompt_order: tuple):
|
95
|
+
"""Convert string names to PromptComponent enum values."""
|
96
|
+
return tuple(
|
97
|
+
PromptComponent(component) if isinstance(component, str) else component
|
98
|
+
for component in prompt_order
|
99
|
+
)
|
100
|
+
|
101
|
+
def _is_valid_plan(self):
|
102
|
+
"""Check if the plan is valid."""
|
103
|
+
combined = self.user_prompt_order + self.system_prompt_order
|
104
|
+
return set(combined) == set(PromptComponent)
|
105
|
+
|
106
|
+
def arrange_components(self, **kwargs) -> Dict[PromptComponent, Prompt]:
|
107
|
+
"""Arrange the components in the order specified by the plan."""
|
108
|
+
# check is valid components passed
|
109
|
+
component_strings = set([pc.value for pc in PromptComponent])
|
110
|
+
if not set(kwargs.keys()) == component_strings:
|
111
|
+
raise ValueError(
|
112
|
+
f"Invalid components passed: {set(kwargs.keys())} but expected {PromptComponent}"
|
113
|
+
)
|
114
|
+
|
115
|
+
user_prompt = PromptList(
|
116
|
+
[kwargs[component.value] for component in self.user_prompt_order]
|
117
|
+
)
|
118
|
+
system_prompt = PromptList(
|
119
|
+
[kwargs[component.value] for component in self.system_prompt_order]
|
120
|
+
)
|
121
|
+
return {"user_prompt": user_prompt, "system_prompt": system_prompt}
|
122
|
+
|
123
|
+
def get_prompts(self, **kwargs) -> Dict[str, Prompt]:
|
124
|
+
"""Get both prompts for the LLM call."""
|
125
|
+
prompts = self.arrange_components(**kwargs)
|
126
|
+
return {
|
127
|
+
"user_prompt": prompts["user_prompt"].reduce(),
|
128
|
+
"system_prompt": prompts["system_prompt"].reduce(),
|
129
|
+
}
|
@@ -0,0 +1,172 @@
|
|
1
|
+
from jinja2 import Environment, meta
|
2
|
+
from typing import List, Optional, Union
|
3
|
+
|
4
|
+
|
5
|
+
class QuestionOptionProcessor:
|
6
|
+
"""
|
7
|
+
Class that manages the processing of question options.
|
8
|
+
These can be provided directly, as a template string, or fetched from prior answers or the scenario.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(self, prompt_constructor):
|
12
|
+
self.prompt_constructor = prompt_constructor
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
def _get_default_options() -> list:
|
16
|
+
"""Return default placeholder options."""
|
17
|
+
return [f"<< Option {i} - Placeholder >>" for i in range(1, 4)]
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
def _parse_template_variable(template_str: str) -> str:
|
21
|
+
"""
|
22
|
+
Extract the variable name from a template string.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
template_str (str): Jinja template string
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
str: Name of the first undefined variable in the template
|
29
|
+
|
30
|
+
>>> QuestionOptionProcessor._parse_template_variable("Here are some {{ options }}")
|
31
|
+
'options'
|
32
|
+
>>> QuestionOptionProcessor._parse_template_variable("Here are some {{ options }} and {{ other }}")
|
33
|
+
Traceback (most recent call last):
|
34
|
+
...
|
35
|
+
ValueError: Multiple variables found in template string
|
36
|
+
>>> QuestionOptionProcessor._parse_template_variable("Here are some")
|
37
|
+
Traceback (most recent call last):
|
38
|
+
...
|
39
|
+
ValueError: No variables found in template string
|
40
|
+
"""
|
41
|
+
env = Environment()
|
42
|
+
parsed_content = env.parse(template_str)
|
43
|
+
undeclared_variables = list(meta.find_undeclared_variables(parsed_content))
|
44
|
+
if not undeclared_variables:
|
45
|
+
raise ValueError("No variables found in template string")
|
46
|
+
if len(undeclared_variables) > 1:
|
47
|
+
raise ValueError("Multiple variables found in template string")
|
48
|
+
return undeclared_variables[0]
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def _get_options_from_scenario(
|
52
|
+
scenario: dict, option_key: str
|
53
|
+
) -> Union[list, None]:
|
54
|
+
"""
|
55
|
+
Try to get options from scenario data.
|
56
|
+
|
57
|
+
>>> from edsl import Scenario
|
58
|
+
>>> scenario = Scenario({"options": ["Option 1", "Option 2"]})
|
59
|
+
>>> QuestionOptionProcessor._get_options_from_scenario(scenario, "options")
|
60
|
+
['Option 1', 'Option 2']
|
61
|
+
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
list | None: List of options if found in scenario, None otherwise
|
65
|
+
"""
|
66
|
+
scenario_options = scenario.get(option_key)
|
67
|
+
return scenario_options if isinstance(scenario_options, list) else None
|
68
|
+
|
69
|
+
@staticmethod
|
70
|
+
def _get_options_from_prior_answers(
|
71
|
+
prior_answers: dict, option_key: str
|
72
|
+
) -> Union[list, None]:
|
73
|
+
"""
|
74
|
+
Try to get options from prior answers.
|
75
|
+
|
76
|
+
prior_answers (dict): Dictionary of prior answers
|
77
|
+
option_key (str): Key to look up in prior answers
|
78
|
+
|
79
|
+
>>> from edsl import QuestionList as Q
|
80
|
+
>>> q = Q.example()
|
81
|
+
>>> q.answer = ["Option 1", "Option 2"]
|
82
|
+
>>> prior_answers = {"options": q}
|
83
|
+
>>> QuestionOptionProcessor._get_options_from_prior_answers(prior_answers, "options")
|
84
|
+
['Option 1', 'Option 2']
|
85
|
+
>>> QuestionOptionProcessor._get_options_from_prior_answers(prior_answers, "wrong_key") is None
|
86
|
+
True
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
list | None: List of options if found in prior answers, None otherwise
|
90
|
+
"""
|
91
|
+
prior_answer = prior_answers.get(option_key)
|
92
|
+
if prior_answer and hasattr(prior_answer, "answer"):
|
93
|
+
if isinstance(prior_answer.answer, list):
|
94
|
+
return prior_answer.answer
|
95
|
+
return None
|
96
|
+
|
97
|
+
def get_question_options(self, question_data: dict) -> list:
|
98
|
+
"""
|
99
|
+
Extract and process question options from question data.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
question_data (dict): Dictionary containing question configuration
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
list: List of question options. Returns default placeholders if no valid options found.
|
106
|
+
|
107
|
+
>>> class MockPromptConstructor:
|
108
|
+
... pass
|
109
|
+
>>> mpc = MockPromptConstructor()
|
110
|
+
>>> from edsl import Scenario
|
111
|
+
>>> mpc.scenario = Scenario({"options": ["Option 1", "Option 2"]})
|
112
|
+
>>> processor = QuestionOptionProcessor(mpc)
|
113
|
+
|
114
|
+
The basic case where options are directly provided:
|
115
|
+
|
116
|
+
>>> question_data = {"question_options": ["Option 1", "Option 2"]}
|
117
|
+
>>> processor.get_question_options(question_data)
|
118
|
+
['Option 1', 'Option 2']
|
119
|
+
|
120
|
+
The case where options are provided as a template string:
|
121
|
+
|
122
|
+
>>> question_data = {"question_options": "{{ options }}"}
|
123
|
+
>>> processor.get_question_options(question_data)
|
124
|
+
['Option 1', 'Option 2']
|
125
|
+
|
126
|
+
The case where there is a templace string but it's in the prior answers:
|
127
|
+
|
128
|
+
>>> class MockQuestion:
|
129
|
+
... pass
|
130
|
+
>>> q0 = MockQuestion()
|
131
|
+
>>> q0.answer = ["Option 1", "Option 2"]
|
132
|
+
>>> mpc.prior_answers_dict = lambda: {'q0': q0}
|
133
|
+
>>> processor = QuestionOptionProcessor(mpc)
|
134
|
+
>>> question_data = {"question_options": "{{ q0 }}"}
|
135
|
+
>>> processor.get_question_options(question_data)
|
136
|
+
['Option 1', 'Option 2']
|
137
|
+
|
138
|
+
The case we're no options are found:
|
139
|
+
>>> processor.get_question_options({"question_options": "{{ poop }}"})
|
140
|
+
['<< Option 1 - Placeholder >>', '<< Option 2 - Placeholder >>', '<< Option 3 - Placeholder >>']
|
141
|
+
|
142
|
+
"""
|
143
|
+
options_entry = question_data.get("question_options")
|
144
|
+
|
145
|
+
# If not a template string, return as is or default
|
146
|
+
if not isinstance(options_entry, str):
|
147
|
+
return options_entry if options_entry else self._get_default_options()
|
148
|
+
|
149
|
+
# Parse template to get variable name
|
150
|
+
option_key = self._parse_template_variable(options_entry)
|
151
|
+
|
152
|
+
# Try getting options from scenario
|
153
|
+
scenario_options = self._get_options_from_scenario(
|
154
|
+
self.prompt_constructor.scenario, option_key
|
155
|
+
)
|
156
|
+
if scenario_options:
|
157
|
+
return scenario_options
|
158
|
+
|
159
|
+
# Try getting options from prior answers
|
160
|
+
prior_answer_options = self._get_options_from_prior_answers(
|
161
|
+
self.prompt_constructor.prior_answers_dict(), option_key
|
162
|
+
)
|
163
|
+
if prior_answer_options:
|
164
|
+
return prior_answer_options
|
165
|
+
|
166
|
+
return self._get_default_options()
|
167
|
+
|
168
|
+
|
169
|
+
if __name__ == "__main__":
|
170
|
+
import doctest
|
171
|
+
|
172
|
+
doctest.testmod()
|
edsl/auto/AutoStudy.py
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
from typing import Optional, TYPE_CHECKING
|
2
|
+
|
3
|
+
from edsl import Model
|
4
|
+
from edsl.auto.StageQuestions import StageQuestions
|
5
|
+
from edsl.auto.StagePersona import StagePersona
|
6
|
+
from edsl.auto.StagePersonaDimensions import StagePersonaDimensions
|
7
|
+
from edsl.auto.StagePersonaDimensionValues import StagePersonaDimensionValues
|
8
|
+
from edsl.auto.StagePersonaDimensionValueRanges import (
|
9
|
+
StagePersonaDimensionValueRanges,
|
10
|
+
)
|
11
|
+
from edsl.auto.StageLabelQuestions import StageLabelQuestions
|
12
|
+
from edsl.auto.StageGenerateSurvey import StageGenerateSurvey
|
13
|
+
|
14
|
+
from edsl.auto.utilities import agent_generator, create_agents, gen_pipeline
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from edsl.surveys.Survey import Survey
|
18
|
+
from edsl.agents.AgentList import AgentList
|
19
|
+
|
20
|
+
|
21
|
+
class AutoStudy:
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
overall_question: str,
|
25
|
+
population: str,
|
26
|
+
model: Optional["Model"] = None,
|
27
|
+
survey: Optional["Survey"] = None,
|
28
|
+
agent_list: Optional["AgentList"] = None,
|
29
|
+
default_num_agents: int = 11,
|
30
|
+
):
|
31
|
+
"""AutoStudy class for generating surveys and agents."""
|
32
|
+
|
33
|
+
self.overall_question = overall_question
|
34
|
+
self.population = population
|
35
|
+
self._survey = survey
|
36
|
+
self._agent_list = agent_list
|
37
|
+
self._agent_list_generator = None
|
38
|
+
self._persona_mapping = None
|
39
|
+
self._results = None
|
40
|
+
self.default_num_agents = default_num_agents
|
41
|
+
self.model = model or Model()
|
42
|
+
|
43
|
+
def to_dict(self):
|
44
|
+
return {
|
45
|
+
"overall_question": self.overall_question,
|
46
|
+
"population": self.population,
|
47
|
+
"survey": self.survey.to_dict(),
|
48
|
+
"persona_mapping": self.persona_mapping.to_dict(),
|
49
|
+
"results": self.results.to_dict(),
|
50
|
+
}
|
51
|
+
|
52
|
+
@property
|
53
|
+
def survey(self):
|
54
|
+
if self._survey is None:
|
55
|
+
self._survey = self._create_survey()
|
56
|
+
return self._survey
|
57
|
+
|
58
|
+
@property
|
59
|
+
def persona_mapping(self):
|
60
|
+
if self._persona_mapping is None:
|
61
|
+
self._persona_mapping = self._create_persona_mapping()
|
62
|
+
return self._persona_mapping
|
63
|
+
|
64
|
+
@property
|
65
|
+
def agent_list_generator(self):
|
66
|
+
if self._agent_list_generator is None:
|
67
|
+
self._agent_list_generator = self._create_agent_list_generator()
|
68
|
+
return self._agent_list_generator
|
69
|
+
|
70
|
+
@property
|
71
|
+
def results(self):
|
72
|
+
if self._results is None:
|
73
|
+
self._results = self._create_results()
|
74
|
+
return self._results
|
75
|
+
|
76
|
+
def _create_survey(self):
|
77
|
+
survey_pipline_stages = [
|
78
|
+
StageQuestions,
|
79
|
+
StageLabelQuestions,
|
80
|
+
StageGenerateSurvey,
|
81
|
+
]
|
82
|
+
survey_pipeline = gen_pipeline(survey_pipline_stages)
|
83
|
+
return survey_pipeline.process(
|
84
|
+
data=survey_pipeline.input(
|
85
|
+
overall_question=self.overall_question, population=self.population
|
86
|
+
)
|
87
|
+
).survey
|
88
|
+
|
89
|
+
def _create_persona_mapping(self):
|
90
|
+
persona_pipeline_stages = [
|
91
|
+
StageQuestions,
|
92
|
+
StagePersona,
|
93
|
+
StagePersonaDimensions,
|
94
|
+
StagePersonaDimensionValues,
|
95
|
+
StagePersonaDimensionValueRanges,
|
96
|
+
]
|
97
|
+
|
98
|
+
persona_pipeline = gen_pipeline(persona_pipeline_stages)
|
99
|
+
sample_agent_results = persona_pipeline.process(
|
100
|
+
persona_pipeline.input(
|
101
|
+
overall_question=overall_question, population=self.population
|
102
|
+
)
|
103
|
+
)
|
104
|
+
return sample_agent_results
|
105
|
+
|
106
|
+
def _create_agent_list_generator(self):
|
107
|
+
return agent_generator(
|
108
|
+
persona=self.persona_mapping.persona,
|
109
|
+
dimension_dict=self.persona_mapping.mapping,
|
110
|
+
)
|
111
|
+
|
112
|
+
def agent_list(self, num_agents):
|
113
|
+
return create_agents(
|
114
|
+
agent_generator=self.agent_list_generator,
|
115
|
+
survey=self.survey,
|
116
|
+
num_agents=num_agents,
|
117
|
+
)
|
118
|
+
|
119
|
+
def _create_results(self, num_agents=None):
|
120
|
+
if num_agents is None:
|
121
|
+
num_agents = self.default_num_agents
|
122
|
+
agent_list = self.agent_list(num_agents)
|
123
|
+
return self.survey.by(agent_list).by(self.model).run()
|
124
|
+
|
125
|
+
|
126
|
+
if __name__ == "__main__":
|
127
|
+
overall_question = "I have an open source Python library for working with LLMs. What are some ways we can market this to others?"
|
128
|
+
auto_study = AutoStudy(overall_question, population="US Adults")
|
129
|
+
|
130
|
+
results = auto_study.results
|