edsl 0.1.39.dev1__py3-none-any.whl → 0.1.39.dev2__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 +169 -116
- edsl/__init__.py +14 -6
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +358 -146
- edsl/agents/AgentList.py +211 -73
- edsl/agents/Invigilator.py +88 -36
- edsl/agents/InvigilatorBase.py +59 -70
- edsl/agents/PromptConstructor.py +117 -219
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionOptionProcessor.py +172 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +0 -1
- edsl/agents/prompt_helpers.py +3 -3
- edsl/config.py +22 -2
- edsl/conversation/car_buying.py +2 -1
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +104 -42
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +21 -14
- edsl/data/CacheEntry.py +12 -15
- edsl/data/CacheHandler.py +33 -12
- edsl/data/__init__.py +4 -3
- edsl/data_transfer_models.py +2 -1
- edsl/enums.py +20 -0
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +12 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/questions.py +24 -6
- edsl/exceptions/scenarios.py +7 -0
- edsl/inference_services/AnthropicService.py +0 -3
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +209 -0
- edsl/inference_services/AwsBedrock.py +0 -2
- edsl/inference_services/AzureAI.py +0 -2
- edsl/inference_services/GoogleService.py +2 -11
- edsl/inference_services/InferenceServiceABC.py +18 -85
- edsl/inference_services/InferenceServicesCollection.py +105 -80
- edsl/inference_services/MistralAIService.py +0 -3
- edsl/inference_services/OpenAIService.py +1 -4
- edsl/inference_services/PerplexityService.py +0 -3
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +11 -8
- edsl/inference_services/data_structures.py +62 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +188 -0
- edsl/jobs/Answers.py +1 -14
- edsl/jobs/FetchInvigilator.py +40 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +48 -0
- edsl/jobs/Jobs.py +102 -243
- edsl/jobs/JobsChecks.py +35 -10
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +5 -3
- edsl/jobs/JobsRemoteInferenceHandler.py +128 -80
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/buckets/BucketCollection.py +44 -3
- edsl/jobs/buckets/TokenBucket.py +53 -21
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +77 -380
- edsl/jobs/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +4 -49
- edsl/jobs/tasks/QuestionTaskCreator.py +21 -19
- edsl/jobs/tasks/TaskHistory.py +14 -15
- edsl/jobs/tasks/task_status_enum.py +0 -2
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +137 -234
- edsl/language_models/ModelList.py +11 -13
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +0 -1
- 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/registry.py +49 -59
- edsl/language_models/repair.py +2 -2
- edsl/language_models/utilities.py +5 -4
- edsl/notebooks/Notebook.py +19 -14
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/prompts/Prompt.py +29 -39
- edsl/questions/AnswerValidatorMixin.py +47 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/LoopProcessor.py +149 -0
- edsl/questions/QuestionBase.py +37 -192
- edsl/questions/QuestionBaseGenMixin.py +52 -48
- edsl/questions/QuestionBasePromptsMixin.py +7 -3
- edsl/questions/QuestionCheckBox.py +1 -1
- edsl/questions/QuestionExtract.py +1 -1
- edsl/questions/QuestionFreeText.py +1 -2
- edsl/questions/QuestionList.py +3 -5
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +66 -22
- edsl/questions/QuestionNumerical.py +1 -3
- edsl/questions/QuestionRank.py +6 -16
- edsl/questions/ResponseValidatorABC.py +37 -11
- edsl/questions/ResponseValidatorFactory.py +28 -0
- edsl/questions/SimpleAskMixin.py +4 -3
- edsl/questions/__init__.py +1 -0
- edsl/questions/derived/QuestionLinearScale.py +6 -3
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +17 -3
- edsl/questions/question_registry.py +1 -1
- 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/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +170 -7
- edsl/results/DatasetExportMixin.py +224 -302
- edsl/results/DatasetTree.py +28 -8
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +192 -206
- edsl/results/Results.py +120 -113
- edsl/results/ResultsExportMixin.py +2 -0
- edsl/results/Selector.py +23 -13
- edsl/results/TableDisplay.py +98 -171
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_renderers.py +118 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DirectoryScanner.py +96 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +118 -239
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +90 -193
- edsl/scenarios/ScenarioHtmlMixin.py +4 -3
- edsl/scenarios/ScenarioJoin.py +10 -6
- edsl/scenarios/ScenarioList.py +383 -240
- edsl/scenarios/ScenarioListExportMixin.py +0 -7
- edsl/scenarios/ScenarioListPdfMixin.py +15 -37
- edsl/scenarios/ScenarioSelector.py +156 -0
- edsl/scenarios/__init__.py +1 -2
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +38 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +5 -12
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/Rule.py +5 -4
- edsl/surveys/RuleCollection.py +25 -27
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +199 -771
- edsl/surveys/SurveyCSS.py +20 -8
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +11 -9
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/descriptors.py +6 -2
- edsl/surveys/instructions/ChangeInstruction.py +1 -2
- edsl/surveys/instructions/Instruction.py +4 -13
- edsl/surveys/instructions/InstructionCollection.py +11 -6
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/utilities.py +35 -23
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/METADATA +12 -10
- edsl-0.1.39.dev2.dist-info/RECORD +352 -0
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.39.dev1.dist-info/RECORD +0 -277
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/WHEEL +0 -0
edsl/questions/QuestionBase.py
CHANGED
@@ -3,30 +3,26 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
from abc import ABC, abstractmethod
|
5
5
|
from typing import Any, Type, Optional, List, Callable, Union, TypedDict
|
6
|
-
import copy
|
7
6
|
|
8
|
-
from edsl.exceptions import (
|
9
|
-
QuestionResponseValidationError,
|
10
|
-
QuestionAnswerValidationError,
|
7
|
+
from edsl.exceptions.questions import (
|
11
8
|
QuestionSerializationError,
|
12
9
|
)
|
13
10
|
from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
|
14
11
|
|
15
|
-
|
16
12
|
from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
|
17
13
|
from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
|
18
|
-
from edsl.Base import PersistenceMixin,
|
14
|
+
from edsl.Base import PersistenceMixin, RepresentationMixin
|
19
15
|
from edsl.BaseDiff import BaseDiff, BaseDiffCollection
|
20
16
|
|
21
17
|
from edsl.questions.SimpleAskMixin import SimpleAskMixin
|
22
18
|
from edsl.questions.QuestionBasePromptsMixin import QuestionBasePromptsMixin
|
23
19
|
from edsl.questions.QuestionBaseGenMixin import QuestionBaseGenMixin
|
24
|
-
from edsl.utilities.
|
20
|
+
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
25
21
|
|
26
22
|
|
27
23
|
class QuestionBase(
|
28
24
|
PersistenceMixin,
|
29
|
-
|
25
|
+
RepresentationMixin,
|
30
26
|
SimpleAskMixin,
|
31
27
|
QuestionBasePromptsMixin,
|
32
28
|
QuestionBaseGenMixin,
|
@@ -36,6 +32,14 @@ class QuestionBase(
|
|
36
32
|
):
|
37
33
|
"""ABC for the Question class. All questions inherit from this class.
|
38
34
|
Some of the constraints on child questions are defined in the RegisterQuestionsMeta metaclass.
|
35
|
+
|
36
|
+
|
37
|
+
Every child class wiill have class attributes of question_type, _response_model and response_validator_class e.g.,
|
38
|
+
|
39
|
+
question_type = "free_text"
|
40
|
+
_response_model = FreeTextResponse
|
41
|
+
response_validator_class = FreeTextResponseValidator
|
42
|
+
|
39
43
|
"""
|
40
44
|
|
41
45
|
question_name: str = QuestionNameDescriptor()
|
@@ -44,37 +48,13 @@ class QuestionBase(
|
|
44
48
|
_answering_instructions = None
|
45
49
|
_question_presentation = None
|
46
50
|
|
47
|
-
@property
|
48
|
-
def response_model(self) -> type["BaseModel"]:
|
49
|
-
if self._response_model is not None:
|
50
|
-
return self._response_model
|
51
|
-
else:
|
52
|
-
return self.create_response_model()
|
53
|
-
|
54
|
-
# region: Validation and simulation methods
|
55
51
|
@property
|
56
52
|
def response_validator(self) -> "ResponseValidatorBase":
|
57
53
|
"""Return the response validator."""
|
58
|
-
|
59
|
-
{
|
60
|
-
"response_model": self.response_model,
|
61
|
-
}
|
62
|
-
| {k: getattr(self, k) for k in self.validator_parameters}
|
63
|
-
| {"exception_to_throw": getattr(self, "exception_to_throw", None)}
|
64
|
-
| {"override_answer": getattr(self, "override_answer", None)}
|
65
|
-
)
|
66
|
-
return self.response_validator_class(**params)
|
67
|
-
|
68
|
-
@property
|
69
|
-
def validator_parameters(self) -> list[str]:
|
70
|
-
"""Return the parameters required for the response validator.
|
54
|
+
from edsl.questions.ResponseValidatorFactory import ResponseValidatorFactory
|
71
55
|
|
72
|
-
|
73
|
-
|
74
|
-
['question_options', 'use_code']
|
75
|
-
|
76
|
-
"""
|
77
|
-
return self.response_validator_class.required_params
|
56
|
+
rvf = ResponseValidatorFactory(self)
|
57
|
+
return rvf.response_validator
|
78
58
|
|
79
59
|
@property
|
80
60
|
def fake_data_factory(self):
|
@@ -82,8 +62,7 @@ class QuestionBase(
|
|
82
62
|
if not hasattr(self, "_fake_data_factory"):
|
83
63
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
84
64
|
|
85
|
-
class FakeData(ModelFactory[self.response_model]):
|
86
|
-
...
|
65
|
+
class FakeData(ModelFactory[self.response_model]): ...
|
87
66
|
|
88
67
|
self._fake_data_factory = FakeData
|
89
68
|
return self._fake_data_factory
|
@@ -110,17 +89,14 @@ class QuestionBase(
|
|
110
89
|
self, answer: dict, replacement_dict: dict = None
|
111
90
|
) -> ValidatedAnswer:
|
112
91
|
"""Validate the answer.
|
113
|
-
>>> from edsl.exceptions import QuestionAnswerValidationError
|
114
|
-
>>> from edsl import QuestionFreeText as Q
|
92
|
+
>>> from edsl.exceptions.questions import QuestionAnswerValidationError
|
93
|
+
>>> from edsl.questions import QuestionFreeText as Q
|
115
94
|
>>> Q.example()._validate_answer({'answer': 'Hello', 'generated_tokens': 'Hello'})
|
116
95
|
{'answer': 'Hello', 'generated_tokens': 'Hello'}
|
117
96
|
"""
|
118
97
|
|
119
98
|
return self.response_validator.validate(answer, replacement_dict)
|
120
99
|
|
121
|
-
# endregion
|
122
|
-
|
123
|
-
# region: Serialization methods
|
124
100
|
@property
|
125
101
|
def name(self) -> str:
|
126
102
|
"Helper function so questions and instructions can use the same access method"
|
@@ -141,7 +117,7 @@ class QuestionBase(
|
|
141
117
|
def data(self) -> dict:
|
142
118
|
"""Return a dictionary of question attributes **except** for question_type.
|
143
119
|
|
144
|
-
>>> from edsl import QuestionFreeText as Q
|
120
|
+
>>> from edsl.questions import QuestionFreeText as Q
|
145
121
|
>>> Q.example().data
|
146
122
|
{'question_name': 'how_are_you', 'question_text': 'How are you?'}
|
147
123
|
"""
|
@@ -186,7 +162,7 @@ class QuestionBase(
|
|
186
162
|
def to_dict(self, add_edsl_version=True):
|
187
163
|
"""Convert the question to a dictionary that includes the question type (used in deserialization).
|
188
164
|
|
189
|
-
>>> from edsl import QuestionFreeText as Q; Q.example().to_dict(add_edsl_version = False)
|
165
|
+
>>> from edsl.questions import QuestionFreeText as Q; Q.example().to_dict(add_edsl_version = False)
|
190
166
|
{'question_name': 'how_are_you', 'question_text': 'How are you?', 'question_type': 'free_text'}
|
191
167
|
"""
|
192
168
|
candidate_data = self.data.copy()
|
@@ -264,12 +240,10 @@ class QuestionBase(
|
|
264
240
|
>>> m.execute_model_call("", "")
|
265
241
|
{'message': [{'text': "Yo, what's up?"}], 'usage': {'prompt_tokens': 1, 'completion_tokens': 1}}
|
266
242
|
>>> Q.run_example(show_answer = True, model = m, disable_remote_cache = True, disable_remote_inference = True)
|
267
|
-
answer.how_are_you
|
268
|
-
--------------------
|
269
|
-
Yo, what's up?
|
243
|
+
Dataset([{'answer.how_are_you': ["Yo, what's up?"]}])
|
270
244
|
"""
|
271
245
|
if model is None:
|
272
|
-
from edsl import Model
|
246
|
+
from edsl.language_models.registry import Model
|
273
247
|
|
274
248
|
model = Model()
|
275
249
|
results = (
|
@@ -282,7 +256,7 @@ class QuestionBase(
|
|
282
256
|
)
|
283
257
|
)
|
284
258
|
if show_answer:
|
285
|
-
return results.select("answer.*")
|
259
|
+
return results.select("answer.*")
|
286
260
|
else:
|
287
261
|
return results
|
288
262
|
|
@@ -293,6 +267,7 @@ class QuestionBase(
|
|
293
267
|
agent=None,
|
294
268
|
disable_remote_cache: bool = False,
|
295
269
|
disable_remote_inference: bool = False,
|
270
|
+
verbose: bool = False,
|
296
271
|
**kwargs,
|
297
272
|
):
|
298
273
|
"""Call the question.
|
@@ -311,6 +286,7 @@ class QuestionBase(
|
|
311
286
|
agent=agent,
|
312
287
|
**kwargs,
|
313
288
|
cache=False,
|
289
|
+
verbose=verbose,
|
314
290
|
disable_remote_cache=disable_remote_cache,
|
315
291
|
disable_remote_inference=disable_remote_inference,
|
316
292
|
)
|
@@ -337,7 +313,7 @@ class QuestionBase(
|
|
337
313
|
"""Call the question asynchronously.
|
338
314
|
|
339
315
|
>>> import asyncio
|
340
|
-
>>> from edsl import QuestionFreeText as Q
|
316
|
+
>>> from edsl.questions import QuestionFreeText as Q
|
341
317
|
>>> m = Q._get_test_model(canned_response = "Blue")
|
342
318
|
>>> q = Q(question_name = "color", question_text = "What is your favorite color?")
|
343
319
|
>>> async def test_run_async(): result = await q.run_async(model=m, disable_remote_inference = True); print(result)
|
@@ -356,27 +332,6 @@ class QuestionBase(
|
|
356
332
|
else:
|
357
333
|
return results
|
358
334
|
|
359
|
-
# endregion
|
360
|
-
|
361
|
-
# region: Magic methods
|
362
|
-
def _repr_html_(self):
|
363
|
-
# from edsl.utilities.utilities import data_to_html
|
364
|
-
|
365
|
-
data = self.to_dict(add_edsl_version=False)
|
366
|
-
# keys = list(data.keys())
|
367
|
-
# values = list(data.values())
|
368
|
-
from tabulate import tabulate
|
369
|
-
|
370
|
-
return tabulate(data.items(), headers=["keys", "values"], tablefmt="html")
|
371
|
-
|
372
|
-
# try:
|
373
|
-
# _ = data.pop("edsl_version")
|
374
|
-
# _ = data.pop("edsl_class_name")
|
375
|
-
# except KeyError:
|
376
|
-
# print("Serialized question lacks edsl version, but is should have it.")
|
377
|
-
|
378
|
-
# return data_to_html(data)
|
379
|
-
|
380
335
|
def __getitem__(self, key: str) -> Any:
|
381
336
|
"""Get an attribute of the question so it can be treated like a dictionary.
|
382
337
|
|
@@ -384,7 +339,10 @@ class QuestionBase(
|
|
384
339
|
>>> Q.example()['question_text']
|
385
340
|
'How are you?'
|
386
341
|
"""
|
387
|
-
|
342
|
+
try:
|
343
|
+
return getattr(self, key)
|
344
|
+
except TypeError:
|
345
|
+
raise KeyError(f"Question has no attribute {key} of type {type(key)}")
|
388
346
|
|
389
347
|
def __repr__(self) -> str:
|
390
348
|
"""Return a string representation of the question. Should be able to be used to reconstruct the question.
|
@@ -414,9 +372,7 @@ class QuestionBase(
|
|
414
372
|
False
|
415
373
|
|
416
374
|
"""
|
417
|
-
|
418
|
-
return False
|
419
|
-
return self.to_dict() == other.to_dict()
|
375
|
+
return hash(self) == hash(other)
|
420
376
|
|
421
377
|
def __sub__(self, other) -> BaseDiff:
|
422
378
|
"""Return the difference between two objects.
|
@@ -433,35 +389,18 @@ class QuestionBase(
|
|
433
389
|
def __add__(self, other_question_or_diff):
|
434
390
|
"""
|
435
391
|
Compose two questions into a single question.
|
436
|
-
|
437
|
-
TODO: Probably getting deprecated.
|
438
|
-
|
439
392
|
"""
|
440
393
|
if isinstance(other_question_or_diff, BaseDiff) or isinstance(
|
441
394
|
other_question_or_diff, BaseDiffCollection
|
442
395
|
):
|
443
396
|
return other_question_or_diff.apply(self)
|
444
397
|
|
445
|
-
# from edsl.questions import compose_questions
|
446
|
-
# return compose_questions(self, other_question_or_diff)
|
447
|
-
|
448
|
-
# def _validate_response(self, response):
|
449
|
-
# """Validate the response from the LLM. Behavior depends on the question type."""
|
450
|
-
# if "answer" not in response:
|
451
|
-
# raise QuestionResponseValidationError(
|
452
|
-
# "Response from LLM does not have an answer"
|
453
|
-
# )
|
454
|
-
# return response
|
455
|
-
|
456
398
|
def _translate_answer_code_to_answer(
|
457
399
|
self, answer, scenario: Optional["Scenario"] = None
|
458
400
|
):
|
459
401
|
"""There is over-ridden by child classes that ask for codes."""
|
460
402
|
return answer
|
461
403
|
|
462
|
-
# endregion
|
463
|
-
|
464
|
-
# region: Forward methods
|
465
404
|
def add_question(self, other: QuestionBase) -> "Survey":
|
466
405
|
"""Add a question to this question by turning them into a survey with two questions.
|
467
406
|
|
@@ -471,10 +410,7 @@ class QuestionBase(
|
|
471
410
|
>>> len(s.questions)
|
472
411
|
2
|
473
412
|
"""
|
474
|
-
|
475
|
-
|
476
|
-
s = Survey([self, other])
|
477
|
-
return s
|
413
|
+
return self.to_survey().add_question(other)
|
478
414
|
|
479
415
|
def to_survey(self) -> "Survey":
|
480
416
|
"""Turn a single question into a survey.
|
@@ -494,15 +430,6 @@ class QuestionBase(
|
|
494
430
|
s = Survey([self])
|
495
431
|
return s.by(*args)
|
496
432
|
|
497
|
-
# endregion
|
498
|
-
|
499
|
-
# region: Display methods
|
500
|
-
def print(self):
|
501
|
-
from rich import print_json
|
502
|
-
import json
|
503
|
-
|
504
|
-
print_json(json.dumps(self.to_dict()))
|
505
|
-
|
506
433
|
def human_readable(self) -> str:
|
507
434
|
"""Print the question in a human readable format.
|
508
435
|
|
@@ -529,97 +456,15 @@ class QuestionBase(
|
|
529
456
|
width: Optional[int] = None,
|
530
457
|
iframe=False,
|
531
458
|
):
|
532
|
-
|
533
|
-
from jinja2 import Template
|
534
|
-
|
535
|
-
if scenario is None:
|
536
|
-
scenario = {}
|
537
|
-
|
538
|
-
prior_answers_dict = {}
|
539
|
-
|
540
|
-
if isinstance(answers, dict):
|
541
|
-
for key, value in answers.items():
|
542
|
-
if not key.endswith("_comment") and not key.endswith(
|
543
|
-
"_generated_tokens"
|
544
|
-
):
|
545
|
-
prior_answers_dict[key] = {"answer": value}
|
546
|
-
|
547
|
-
# breakpoint()
|
548
|
-
|
549
|
-
base_template = """
|
550
|
-
<div id="{{ question_name }}" class="survey_question" data-type="{{ question_type }}">
|
551
|
-
{% if include_question_name %}
|
552
|
-
<p>question_name: {{ question_name }}</p>
|
553
|
-
{% endif %}
|
554
|
-
<p class="question_text">{{ question_text }}</p>
|
555
|
-
{{ question_content }}
|
556
|
-
</div>
|
557
|
-
"""
|
558
|
-
if not hasattr(self, "question_type"):
|
559
|
-
self.question_type = "unknown"
|
560
|
-
|
561
|
-
if hasattr(self, "question_html_content"):
|
562
|
-
question_content = self.question_html_content
|
563
|
-
else:
|
564
|
-
question_content = Template("")
|
459
|
+
from edsl.questions.HTMLQuestion import HTMLQuestion
|
565
460
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
"scenario": scenario,
|
570
|
-
"agent": agent,
|
571
|
-
} | prior_answers_dict
|
572
|
-
|
573
|
-
# Render the question text
|
574
|
-
try:
|
575
|
-
question_text = Template(self.question_text).render(context)
|
576
|
-
except Exception as e:
|
577
|
-
print(
|
578
|
-
f"Error rendering question: question_text = {self.question_text}, error = {e}"
|
579
|
-
)
|
580
|
-
question_text = self.question_text
|
581
|
-
|
582
|
-
try:
|
583
|
-
question_content = Template(question_content).render(context)
|
584
|
-
except Exception as e:
|
585
|
-
print(
|
586
|
-
f"Error rendering question: question_content = {question_content}, error = {e}"
|
587
|
-
)
|
588
|
-
question_content = question_content
|
589
|
-
|
590
|
-
try:
|
591
|
-
params = {
|
592
|
-
"question_name": self.question_name,
|
593
|
-
"question_text": question_text,
|
594
|
-
"question_type": self.question_type,
|
595
|
-
"question_content": question_content,
|
596
|
-
"include_question_name": include_question_name,
|
597
|
-
}
|
598
|
-
except Exception as e:
|
599
|
-
raise ValueError(
|
600
|
-
f"Error rendering question: params = {params}, error = {e}"
|
601
|
-
)
|
602
|
-
rendered_html = base_template.render(**params)
|
603
|
-
|
604
|
-
if iframe:
|
605
|
-
import html
|
606
|
-
from IPython.display import display, HTML
|
607
|
-
|
608
|
-
height = height or 200
|
609
|
-
width = width or 600
|
610
|
-
escaped_output = html.escape(rendered_html)
|
611
|
-
# escaped_output = rendered_html
|
612
|
-
iframe = f""""
|
613
|
-
<iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
|
614
|
-
"""
|
615
|
-
display(HTML(iframe))
|
616
|
-
return None
|
617
|
-
|
618
|
-
return rendered_html
|
461
|
+
return HTMLQuestion(self).html(
|
462
|
+
scenario, agent, answers, include_question_name, height, width, iframe
|
463
|
+
)
|
619
464
|
|
620
465
|
@classmethod
|
621
466
|
def example_model(cls):
|
622
|
-
from edsl import Model
|
467
|
+
from edsl.language_models.registry import Model
|
623
468
|
|
624
469
|
q = cls.example()
|
625
470
|
m = Model("test", canned_response=cls._simulate_answer(q)["answer"])
|
@@ -21,7 +21,7 @@ class QuestionBaseGenMixin:
|
|
21
21
|
def option_permutations(self) -> list[QuestionBase]:
|
22
22
|
"""Return a list of questions with all possible permutations of the options.
|
23
23
|
|
24
|
-
>>> from edsl import QuestionMultipleChoice as Q
|
24
|
+
>>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice as Q
|
25
25
|
>>> len(Q.example().option_permutations())
|
26
26
|
24
|
27
27
|
"""
|
@@ -44,61 +44,65 @@ class QuestionBaseGenMixin:
|
|
44
44
|
|
45
45
|
:param scenario_list: The list of scenarios to loop through.
|
46
46
|
|
47
|
-
>>> from edsl import QuestionFreeText
|
48
|
-
>>> from edsl import ScenarioList
|
47
|
+
>>> from edsl.questions.QuestionFreeText import QuestionFreeText
|
48
|
+
>>> from edsl.scenarios.ScenarioList import ScenarioList
|
49
49
|
>>> q = QuestionFreeText(question_text = "What are your thoughts on: {{ subject}}?", question_name = "base_{{subject}}")
|
50
50
|
>>> len(q.loop(ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])))
|
51
51
|
3
|
52
|
-
|
53
52
|
"""
|
54
|
-
from
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
53
|
+
from edsl.questions.LoopProcessor import LoopProcessor
|
54
|
+
|
55
|
+
lp = LoopProcessor(self)
|
56
|
+
return lp.process_templates(scenario_list)
|
57
|
+
|
58
|
+
# from jinja2 import Environment
|
59
|
+
# from edsl.questions.QuestionBase import QuestionBase
|
60
|
+
|
61
|
+
# starting_name = self.question_name
|
62
|
+
# questions = []
|
63
|
+
# for index, scenario in enumerate(scenario_list):
|
64
|
+
# env = Environment()
|
65
|
+
# new_data = self.to_dict().copy()
|
66
|
+
# for key, value in [(k, v) for k, v in new_data.items() if v is not None]:
|
67
|
+
# if (
|
68
|
+
# isinstance(value, str) or isinstance(value, int)
|
69
|
+
# ) and key != "question_options":
|
70
|
+
# new_data[key] = env.from_string(value).render(scenario)
|
71
|
+
# elif isinstance(value, list):
|
72
|
+
# new_data[key] = [
|
73
|
+
# env.from_string(v).render(scenario) if isinstance(v, str) else v
|
74
|
+
# for v in value
|
75
|
+
# ]
|
76
|
+
# elif isinstance(value, dict):
|
77
|
+
# new_data[key] = {
|
78
|
+
# (
|
79
|
+
# env.from_string(k).render(scenario)
|
80
|
+
# if isinstance(k, str)
|
81
|
+
# else k
|
82
|
+
# ): (
|
83
|
+
# env.from_string(v).render(scenario)
|
84
|
+
# if isinstance(v, str)
|
85
|
+
# else v
|
86
|
+
# )
|
87
|
+
# for k, v in value.items()
|
88
|
+
# }
|
89
|
+
# elif key == "question_options" and isinstance(value, str):
|
90
|
+
# new_data[key] = value
|
91
|
+
# else:
|
92
|
+
# raise ValueError(
|
93
|
+
# f"Unexpected value type: {type(value)} for key '{key}'"
|
94
|
+
# )
|
95
|
+
|
96
|
+
# if new_data["question_name"] == starting_name:
|
97
|
+
# new_data["question_name"] = new_data["question_name"] + f"_{index}"
|
98
|
+
|
99
|
+
# questions.append(QuestionBase.from_dict(new_data))
|
100
|
+
# return questions
|
97
101
|
|
98
102
|
def render(self, replacement_dict: dict) -> "QuestionBase":
|
99
103
|
"""Render the question components as jinja2 templates with the replacement dictionary."""
|
100
104
|
from jinja2 import Environment
|
101
|
-
from edsl import Scenario
|
105
|
+
from edsl.scenarios.Scenario import Scenario
|
102
106
|
|
103
107
|
strings_only_replacement_dict = {
|
104
108
|
k: v for k, v in replacement_dict.items() if not isinstance(v, Scenario)
|
@@ -1,8 +1,6 @@
|
|
1
1
|
from importlib import resources
|
2
2
|
from typing import Optional
|
3
|
-
from edsl.prompts import Prompt
|
4
3
|
from edsl.exceptions.questions import QuestionAnswerValidationError
|
5
|
-
|
6
4
|
from functools import lru_cache
|
7
5
|
|
8
6
|
|
@@ -71,7 +69,7 @@ class QuestionBasePromptsMixin:
|
|
71
69
|
>>> q.get_instructions(model = "gpt3")
|
72
70
|
Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
|
73
71
|
"""
|
74
|
-
from edsl import Model
|
72
|
+
from edsl.language_models.registry import Model
|
75
73
|
|
76
74
|
if not hasattr(self, "_model_instructions"):
|
77
75
|
self._model_instructions = {}
|
@@ -122,6 +120,8 @@ class QuestionBasePromptsMixin:
|
|
122
120
|
template_text = template_manager.get_template(
|
123
121
|
cls.question_type, "answering_instructions.jinja"
|
124
122
|
)
|
123
|
+
from edsl.prompts import Prompt
|
124
|
+
|
125
125
|
return Prompt(text=template_text)
|
126
126
|
|
127
127
|
@classmethod
|
@@ -129,6 +129,8 @@ class QuestionBasePromptsMixin:
|
|
129
129
|
template_text = template_manager.get_template(
|
130
130
|
cls.question_type, "question_presentation.jinja"
|
131
131
|
)
|
132
|
+
from edsl.prompts import Prompt
|
133
|
+
|
132
134
|
return Prompt(text=template_text)
|
133
135
|
|
134
136
|
@property
|
@@ -182,6 +184,8 @@ class QuestionBasePromptsMixin:
|
|
182
184
|
@property
|
183
185
|
def new_default_instructions(self) -> "Prompt":
|
184
186
|
"This is set up as a property because there are mutable question values that determine how it is rendered."
|
187
|
+
from edsl.prompts import Prompt
|
188
|
+
|
185
189
|
return Prompt(self.question_presentation) + Prompt(self.answering_instructions)
|
186
190
|
|
187
191
|
@property
|
@@ -16,7 +16,7 @@ from pydantic import field_validator
|
|
16
16
|
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
17
17
|
from edsl.questions.ResponseValidatorABC import BaseResponse
|
18
18
|
|
19
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
19
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
20
20
|
|
21
21
|
from pydantic import BaseModel, Field, conlist
|
22
22
|
from typing import List, Literal, Optional, Annotated
|
@@ -8,7 +8,7 @@ from edsl.questions.descriptors import AnswerTemplateDescriptor
|
|
8
8
|
|
9
9
|
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
10
10
|
from edsl.questions.ResponseValidatorABC import BaseResponse
|
11
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
11
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
12
12
|
from edsl.questions.decorators import inject_exception
|
13
13
|
|
14
14
|
from typing import Dict, Any
|
@@ -7,13 +7,12 @@ from pydantic import field_validator
|
|
7
7
|
from edsl.questions.QuestionBase import QuestionBase
|
8
8
|
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
9
9
|
|
10
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
10
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
11
11
|
from edsl.questions.decorators import inject_exception
|
12
12
|
|
13
13
|
from pydantic import BaseModel
|
14
14
|
from typing import Optional, Any, List
|
15
15
|
|
16
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
17
16
|
from edsl.prompts.Prompt import Prompt
|
18
17
|
|
19
18
|
|
edsl/questions/QuestionList.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import
|
3
|
-
|
2
|
+
import json
|
3
|
+
|
4
4
|
from typing import Any, Optional, Union
|
5
5
|
from edsl.questions.QuestionBase import QuestionBase
|
6
6
|
from edsl.questions.descriptors import IntegerOrNoneDescriptor
|
@@ -10,9 +10,7 @@ from pydantic import field_validator, Field
|
|
10
10
|
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
11
11
|
from edsl.questions.ResponseValidatorABC import BaseResponse
|
12
12
|
|
13
|
-
from edsl.exceptions import QuestionAnswerValidationError
|
14
|
-
import textwrap
|
15
|
-
import json
|
13
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
16
14
|
|
17
15
|
from json_repair import repair_json
|
18
16
|
|