edsl 0.1.39__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 +0 -28
- edsl/__init__.py +1 -1
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +17 -9
- edsl/agents/Invigilator.py +14 -13
- edsl/agents/InvigilatorBase.py +1 -4
- edsl/agents/PromptConstructor.py +22 -42
- edsl/agents/QuestionInstructionPromptBuilder.py +1 -1
- edsl/auto/AutoStudy.py +5 -18
- edsl/auto/StageBase.py +40 -53
- edsl/auto/StageQuestions.py +1 -2
- edsl/auto/utilities.py +6 -0
- edsl/coop/coop.py +5 -21
- edsl/data/Cache.py +18 -29
- edsl/data/CacheHandler.py +2 -0
- edsl/data/RemoteCacheSync.py +46 -154
- edsl/enums.py +0 -7
- edsl/inference_services/AnthropicService.py +16 -38
- edsl/inference_services/AvailableModelFetcher.py +1 -7
- edsl/inference_services/GoogleService.py +1 -5
- edsl/inference_services/InferenceServicesCollection.py +2 -18
- edsl/inference_services/OpenAIService.py +31 -46
- edsl/inference_services/TestService.py +3 -1
- edsl/inference_services/TogetherAIService.py +3 -5
- edsl/inference_services/data_structures.py +2 -74
- edsl/jobs/AnswerQuestionFunctionConstructor.py +113 -148
- edsl/jobs/FetchInvigilator.py +3 -10
- edsl/jobs/InterviewsConstructor.py +4 -6
- edsl/jobs/Jobs.py +233 -299
- edsl/jobs/JobsChecks.py +2 -2
- edsl/jobs/JobsPrompts.py +1 -1
- edsl/jobs/JobsRemoteInferenceHandler.py +136 -160
- edsl/jobs/interviews/Interview.py +42 -80
- edsl/jobs/runners/JobsRunnerAsyncio.py +358 -88
- edsl/jobs/runners/JobsRunnerStatus.py +165 -133
- edsl/jobs/tasks/TaskHistory.py +3 -24
- edsl/language_models/LanguageModel.py +4 -59
- edsl/language_models/ModelList.py +8 -19
- edsl/language_models/__init__.py +1 -1
- edsl/language_models/registry.py +180 -0
- edsl/language_models/repair.py +1 -1
- edsl/questions/QuestionBase.py +26 -35
- edsl/questions/{question_base_gen_mixin.py → QuestionBaseGenMixin.py} +49 -52
- edsl/questions/QuestionBasePromptsMixin.py +1 -1
- edsl/questions/QuestionBudget.py +1 -1
- edsl/questions/QuestionCheckBox.py +2 -2
- edsl/questions/QuestionExtract.py +7 -5
- edsl/questions/QuestionFreeText.py +1 -1
- edsl/questions/QuestionList.py +15 -9
- edsl/questions/QuestionMatrix.py +1 -1
- edsl/questions/QuestionMultipleChoice.py +1 -1
- edsl/questions/QuestionNumerical.py +1 -1
- edsl/questions/QuestionRank.py +1 -1
- edsl/questions/{response_validator_abc.py → ResponseValidatorABC.py} +18 -6
- edsl/questions/{response_validator_factory.py → ResponseValidatorFactory.py} +1 -7
- edsl/questions/SimpleAskMixin.py +1 -1
- edsl/questions/__init__.py +1 -1
- edsl/results/DatasetExportMixin.py +119 -60
- edsl/results/Result.py +3 -109
- edsl/results/Results.py +39 -50
- edsl/scenarios/FileStore.py +0 -32
- edsl/scenarios/ScenarioList.py +7 -35
- edsl/scenarios/handlers/csv.py +0 -11
- edsl/surveys/Survey.py +20 -71
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev2.dist-info}/METADATA +1 -1
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev2.dist-info}/RECORD +78 -84
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev2.dist-info}/WHEEL +1 -1
- edsl/jobs/async_interview_runner.py +0 -138
- edsl/jobs/check_survey_scenario_compatibility.py +0 -85
- edsl/jobs/data_structures.py +0 -120
- edsl/jobs/results_exceptions_handler.py +0 -98
- edsl/language_models/model.py +0 -256
- edsl/questions/data_structures.py +0 -20
- edsl/results/file_exports.py +0 -252
- /edsl/agents/{question_option_processor.py → QuestionOptionProcessor.py} +0 -0
- /edsl/questions/{answer_validator_mixin.py → AnswerValidatorMixin.py} +0 -0
- /edsl/questions/{loop_processor.py → LoopProcessor.py} +0 -0
- /edsl/questions/{register_questions_meta.py → RegisterQuestionsMeta.py} +0 -0
- /edsl/results/{results_fetch_mixin.py → ResultsFetchMixin.py} +0 -0
- /edsl/results/{results_tools_mixin.py → ResultsToolsMixin.py} +0 -0
- /edsl/results/{results_selector.py → Selector.py} +0 -0
- /edsl/scenarios/{directory_scanner.py → DirectoryScanner.py} +0 -0
- /edsl/scenarios/{scenario_join.py → ScenarioJoin.py} +0 -0
- /edsl/scenarios/{scenario_selector.py → ScenarioSelector.py} +0 -0
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev2.dist-info}/LICENSE +0 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
import textwrap
|
2
|
+
from random import random
|
3
|
+
from edsl.config import CONFIG
|
4
|
+
from functools import lru_cache
|
5
|
+
from edsl.utilities.PrettyList import PrettyList
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
|
9
|
+
def get_model_class(model_name, registry=None):
|
10
|
+
from edsl.inference_services.registry import default
|
11
|
+
|
12
|
+
registry = registry or default
|
13
|
+
factory = registry.create_model_factory(model_name)
|
14
|
+
return factory
|
15
|
+
|
16
|
+
|
17
|
+
class Meta(type):
|
18
|
+
def __repr__(cls):
|
19
|
+
return textwrap.dedent(
|
20
|
+
f"""\
|
21
|
+
Available models: {cls.available()}
|
22
|
+
|
23
|
+
To create an instance, you can do:
|
24
|
+
>>> m = Model('gpt-4-1106-preview', temperature=0.5, ...)
|
25
|
+
|
26
|
+
To get the default model, you can leave out the model name.
|
27
|
+
To see the available models, you can do:
|
28
|
+
>>> Model.available()
|
29
|
+
"""
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
class Model(metaclass=Meta):
|
34
|
+
default_model = CONFIG.get("EDSL_DEFAULT_MODEL")
|
35
|
+
|
36
|
+
def __new__(
|
37
|
+
cls, model_name=None, registry=None, service_name=None, *args, **kwargs
|
38
|
+
):
|
39
|
+
# Map index to the respective subclass
|
40
|
+
if model_name is None:
|
41
|
+
model_name = (
|
42
|
+
cls.default_model
|
43
|
+
) # when model_name is None, use the default model, set in the config file
|
44
|
+
from edsl.inference_services.registry import default
|
45
|
+
|
46
|
+
registry = registry or default
|
47
|
+
|
48
|
+
if isinstance(model_name, int): # can refer to a model by index
|
49
|
+
model_name = cls.available(name_only=True)[model_name]
|
50
|
+
|
51
|
+
factory = registry.create_model_factory(model_name, service_name=service_name)
|
52
|
+
return factory(*args, **kwargs)
|
53
|
+
|
54
|
+
@classmethod
|
55
|
+
def add_model(cls, service_name, model_name):
|
56
|
+
from edsl.inference_services.registry import default
|
57
|
+
|
58
|
+
registry = default
|
59
|
+
registry.add_model(service_name, model_name)
|
60
|
+
|
61
|
+
@classmethod
|
62
|
+
def service_classes(cls, registry=None):
|
63
|
+
from edsl.inference_services.registry import default
|
64
|
+
|
65
|
+
registry = registry or default
|
66
|
+
return [r for r in registry.services]
|
67
|
+
|
68
|
+
@classmethod
|
69
|
+
def services(cls, registry=None):
|
70
|
+
from edsl.inference_services.registry import default
|
71
|
+
|
72
|
+
registry = registry or default
|
73
|
+
return PrettyList(
|
74
|
+
[r._inference_service_ for r in registry.services], columns=["Service Name"]
|
75
|
+
)
|
76
|
+
|
77
|
+
@classmethod
|
78
|
+
def key_info(cls):
|
79
|
+
from edsl.language_models.key_management import KeyLookupCollection
|
80
|
+
from edsl.scenarios import Scenario, ScenarioList
|
81
|
+
|
82
|
+
klc = KeyLookupCollection()
|
83
|
+
klc.add_key_lookup(fetch_order=None)
|
84
|
+
sl = ScenarioList()
|
85
|
+
for service, entry in list(klc.data.values())[0].items():
|
86
|
+
sl.append(Scenario({"service": service} | entry.to_dict()))
|
87
|
+
return sl.to_dataset()
|
88
|
+
|
89
|
+
@classmethod
|
90
|
+
def available(
|
91
|
+
cls,
|
92
|
+
search_term: str = None,
|
93
|
+
name_only: bool = False,
|
94
|
+
registry=None,
|
95
|
+
service: Optional[str] = None,
|
96
|
+
):
|
97
|
+
from edsl.inference_services.registry import default
|
98
|
+
|
99
|
+
registry = registry or default
|
100
|
+
# full_list = registry.available()
|
101
|
+
|
102
|
+
if service is not None:
|
103
|
+
if service not in cls.services(registry=registry):
|
104
|
+
raise ValueError(f"Service {service} not found in available services.")
|
105
|
+
|
106
|
+
# import time
|
107
|
+
# start = time.time()
|
108
|
+
full_list = registry.available(service=service)
|
109
|
+
# end = time.time()
|
110
|
+
# print(f"Time taken to get available models: {end-start}")
|
111
|
+
|
112
|
+
if search_term is None:
|
113
|
+
if name_only:
|
114
|
+
return PrettyList(
|
115
|
+
[m.model_name for m in full_list],
|
116
|
+
columns=["Model Name"],
|
117
|
+
)
|
118
|
+
else:
|
119
|
+
return PrettyList(
|
120
|
+
[[m.model_name, m.service_name] for m in full_list],
|
121
|
+
columns=["Model Name", "Service Name"],
|
122
|
+
)
|
123
|
+
else:
|
124
|
+
filtered_results = [
|
125
|
+
m
|
126
|
+
for m in full_list
|
127
|
+
if search_term in m.model_name or search_term in m.service_name
|
128
|
+
]
|
129
|
+
if name_only:
|
130
|
+
return PrettyList(
|
131
|
+
[m.model_name for m in filtered_results],
|
132
|
+
columns=["Model Name"],
|
133
|
+
)
|
134
|
+
else:
|
135
|
+
return PrettyList(
|
136
|
+
[[m.model_name, m.service_name] for m in full_list],
|
137
|
+
columns=["Model Name", "Service Name"],
|
138
|
+
)
|
139
|
+
|
140
|
+
@classmethod
|
141
|
+
def check_models(cls, verbose=False):
|
142
|
+
print("Checking all available models...\n")
|
143
|
+
for model in cls.available(name_only=True):
|
144
|
+
print(f"Now checking: {model}")
|
145
|
+
try:
|
146
|
+
m = cls(model)
|
147
|
+
except Exception as e:
|
148
|
+
print(f"Error creating instance of {model}: {e}")
|
149
|
+
continue
|
150
|
+
try:
|
151
|
+
results = m.hello(verbose)
|
152
|
+
if verbose:
|
153
|
+
print(f"Results from model call: {results}")
|
154
|
+
except Exception as e:
|
155
|
+
print(f"Error calling 'hello' on {model}: {e}")
|
156
|
+
continue
|
157
|
+
print("OK!")
|
158
|
+
print("\n")
|
159
|
+
|
160
|
+
@classmethod
|
161
|
+
def example(cls, randomize: bool = False) -> "Model":
|
162
|
+
"""
|
163
|
+
Returns an example Model instance.
|
164
|
+
|
165
|
+
:param randomize: If True, the temperature is set to a random decimal between 0 and 1.
|
166
|
+
"""
|
167
|
+
temperature = 0.5 if not randomize else round(random(), 2)
|
168
|
+
model_name = cls.default_model
|
169
|
+
return cls(model_name, temperature=temperature)
|
170
|
+
|
171
|
+
|
172
|
+
if __name__ == "__main__":
|
173
|
+
import doctest
|
174
|
+
|
175
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
176
|
+
|
177
|
+
available = Model.available()
|
178
|
+
m = Model("gpt-4-1106-preview")
|
179
|
+
results = m.execute_model_call("Hello world")
|
180
|
+
print(results)
|
edsl/language_models/repair.py
CHANGED
edsl/questions/QuestionBase.py
CHANGED
@@ -2,31 +2,23 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
from abc import ABC, abstractmethod
|
5
|
-
from typing import Any, Type, Optional, List, Callable, Union, TypedDict
|
5
|
+
from typing import Any, Type, Optional, List, Callable, Union, TypedDict
|
6
6
|
|
7
7
|
from edsl.exceptions.questions import (
|
8
8
|
QuestionSerializationError,
|
9
9
|
)
|
10
10
|
from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
|
11
11
|
|
12
|
-
from edsl.questions.
|
13
|
-
from edsl.questions.
|
12
|
+
from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
|
13
|
+
from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
|
14
14
|
from edsl.Base import PersistenceMixin, RepresentationMixin
|
15
15
|
from edsl.BaseDiff import BaseDiff, BaseDiffCollection
|
16
16
|
|
17
17
|
from edsl.questions.SimpleAskMixin import SimpleAskMixin
|
18
18
|
from edsl.questions.QuestionBasePromptsMixin import QuestionBasePromptsMixin
|
19
|
-
from edsl.questions.
|
19
|
+
from edsl.questions.QuestionBaseGenMixin import QuestionBaseGenMixin
|
20
20
|
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
21
21
|
|
22
|
-
if TYPE_CHECKING:
|
23
|
-
from edsl.questions.response_validator_abc import ResponseValidatorABC
|
24
|
-
from edsl.language_models.LanguageModel import LanguageModel
|
25
|
-
from edsl.results.Results import Results
|
26
|
-
from edsl.agents.Agent import Agent
|
27
|
-
from edsl.surveys.Survey import Survey
|
28
|
-
from edsl.jobs.Jobs import Jobs
|
29
|
-
|
30
22
|
|
31
23
|
class QuestionBase(
|
32
24
|
PersistenceMixin,
|
@@ -57,17 +49,13 @@ class QuestionBase(
|
|
57
49
|
_question_presentation = None
|
58
50
|
|
59
51
|
@property
|
60
|
-
def response_validator(self) -> "
|
52
|
+
def response_validator(self) -> "ResponseValidatorBase":
|
61
53
|
"""Return the response validator."""
|
62
|
-
from edsl.questions.
|
54
|
+
from edsl.questions.ResponseValidatorFactory import ResponseValidatorFactory
|
63
55
|
|
64
56
|
rvf = ResponseValidatorFactory(self)
|
65
57
|
return rvf.response_validator
|
66
58
|
|
67
|
-
def duplicate(self):
|
68
|
-
"""Return a duplicate of the question."""
|
69
|
-
return self.from_dict(self.to_dict())
|
70
|
-
|
71
59
|
@property
|
72
60
|
def fake_data_factory(self):
|
73
61
|
"""Return the fake data factory."""
|
@@ -171,7 +159,7 @@ class QuestionBase(
|
|
171
159
|
|
172
160
|
return candidate_data
|
173
161
|
|
174
|
-
def to_dict(self, add_edsl_version
|
162
|
+
def to_dict(self, add_edsl_version=True):
|
175
163
|
"""Convert the question to a dictionary that includes the question type (used in deserialization).
|
176
164
|
|
177
165
|
>>> from edsl.questions import QuestionFreeText as Q; Q.example().to_dict(add_edsl_version = False)
|
@@ -225,6 +213,9 @@ class QuestionBase(
|
|
225
213
|
|
226
214
|
return question_class(**local_data)
|
227
215
|
|
216
|
+
# endregion
|
217
|
+
|
218
|
+
# region: Running methods
|
228
219
|
@classmethod
|
229
220
|
def _get_test_model(self, canned_response: Optional[str] = None) -> "LanguageModel":
|
230
221
|
"""Get a test model for the question."""
|
@@ -252,7 +243,7 @@ class QuestionBase(
|
|
252
243
|
Dataset([{'answer.how_are_you': ["Yo, what's up?"]}])
|
253
244
|
"""
|
254
245
|
if model is None:
|
255
|
-
from edsl.language_models.
|
246
|
+
from edsl.language_models.registry import Model
|
256
247
|
|
257
248
|
model = Model()
|
258
249
|
results = (
|
@@ -271,22 +262,21 @@ class QuestionBase(
|
|
271
262
|
|
272
263
|
def __call__(
|
273
264
|
self,
|
274
|
-
just_answer
|
275
|
-
model
|
276
|
-
agent
|
265
|
+
just_answer=True,
|
266
|
+
model=None,
|
267
|
+
agent=None,
|
277
268
|
disable_remote_cache: bool = False,
|
278
269
|
disable_remote_inference: bool = False,
|
279
270
|
verbose: bool = False,
|
280
271
|
**kwargs,
|
281
|
-
)
|
272
|
+
):
|
282
273
|
"""Call the question.
|
283
274
|
|
284
275
|
|
285
276
|
>>> from edsl import QuestionFreeText as Q
|
286
|
-
>>>
|
287
|
-
>>> m = Model("test", canned_response = "Yo, what's up?")
|
277
|
+
>>> m = Q._get_test_model(canned_response = "Yo, what's up?")
|
288
278
|
>>> q = Q(question_name = "color", question_text = "What is your favorite color?")
|
289
|
-
>>> q(model = m, disable_remote_cache = True, disable_remote_inference = True
|
279
|
+
>>> q(model = m, disable_remote_cache = True, disable_remote_inference = True)
|
290
280
|
"Yo, what's up?"
|
291
281
|
|
292
282
|
"""
|
@@ -295,6 +285,7 @@ class QuestionBase(
|
|
295
285
|
model=model,
|
296
286
|
agent=agent,
|
297
287
|
**kwargs,
|
288
|
+
cache=False,
|
298
289
|
verbose=verbose,
|
299
290
|
disable_remote_cache=disable_remote_cache,
|
300
291
|
disable_remote_inference=disable_remote_inference,
|
@@ -306,16 +297,15 @@ class QuestionBase(
|
|
306
297
|
|
307
298
|
def run(self, *args, **kwargs) -> "Results":
|
308
299
|
"""Turn a single question into a survey and runs it."""
|
309
|
-
|
300
|
+
from edsl.surveys.Survey import Survey
|
310
301
|
|
311
|
-
|
312
|
-
|
313
|
-
return self.to_survey().to_jobs().using(*args, **kwargs)
|
302
|
+
s = self.to_survey()
|
303
|
+
return s.run(*args, **kwargs)
|
314
304
|
|
315
305
|
async def run_async(
|
316
306
|
self,
|
317
307
|
just_answer: bool = True,
|
318
|
-
model: Optional["
|
308
|
+
model: Optional["Model"] = None,
|
319
309
|
agent: Optional["Agent"] = None,
|
320
310
|
disable_remote_inference: bool = False,
|
321
311
|
**kwargs,
|
@@ -326,7 +316,7 @@ class QuestionBase(
|
|
326
316
|
>>> from edsl.questions import QuestionFreeText as Q
|
327
317
|
>>> m = Q._get_test_model(canned_response = "Blue")
|
328
318
|
>>> q = Q(question_name = "color", question_text = "What is your favorite color?")
|
329
|
-
>>> async def test_run_async(): result = await q.run_async(model=m, disable_remote_inference = True
|
319
|
+
>>> async def test_run_async(): result = await q.run_async(model=m, disable_remote_inference = True); print(result)
|
330
320
|
>>> asyncio.run(test_run_async())
|
331
321
|
Blue
|
332
322
|
"""
|
@@ -430,7 +420,8 @@ class QuestionBase(
|
|
430
420
|
"""
|
431
421
|
from edsl.surveys.Survey import Survey
|
432
422
|
|
433
|
-
|
423
|
+
s = Survey([self])
|
424
|
+
return s
|
434
425
|
|
435
426
|
def by(self, *args) -> "Jobs":
|
436
427
|
"""Turn a single question into a survey and then a Job."""
|
@@ -473,7 +464,7 @@ class QuestionBase(
|
|
473
464
|
|
474
465
|
@classmethod
|
475
466
|
def example_model(cls):
|
476
|
-
from edsl.language_models.
|
467
|
+
from edsl.language_models.registry import Model
|
477
468
|
|
478
469
|
q = cls.example()
|
479
470
|
m = Model("test", canned_response=cls._simulate_answer(q)["answer"])
|
@@ -1,16 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import copy
|
3
3
|
import itertools
|
4
|
-
from typing import Optional, List, Callable, Type
|
5
|
-
|
6
|
-
if TYPE_CHECKING:
|
7
|
-
from edsl.questions.QuestionBase import QuestionBase
|
8
|
-
from edsl.scenarios.ScenarioList import ScenarioList
|
4
|
+
from typing import Optional, List, Callable, Type
|
5
|
+
from typing import TypeVar
|
9
6
|
|
10
7
|
|
11
8
|
class QuestionBaseGenMixin:
|
12
|
-
"""Mixin for QuestionBase."""
|
13
|
-
|
14
9
|
def copy(self) -> QuestionBase:
|
15
10
|
"""Return a deep copy of the question.
|
16
11
|
|
@@ -44,31 +39,6 @@ class QuestionBaseGenMixin:
|
|
44
39
|
questions.append(question)
|
45
40
|
return questions
|
46
41
|
|
47
|
-
def draw(self) -> "QuestionBase":
|
48
|
-
"""Return a new question with a randomly selected permutation of the options.
|
49
|
-
|
50
|
-
If the question has no options, returns a copy of the original question.
|
51
|
-
|
52
|
-
>>> from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice as Q
|
53
|
-
>>> q = Q.example()
|
54
|
-
>>> drawn = q.draw()
|
55
|
-
>>> len(drawn.question_options) == len(q.question_options)
|
56
|
-
True
|
57
|
-
>>> q is drawn
|
58
|
-
False
|
59
|
-
"""
|
60
|
-
|
61
|
-
if not hasattr(self, "question_options"):
|
62
|
-
return copy.deepcopy(self)
|
63
|
-
|
64
|
-
import random
|
65
|
-
|
66
|
-
question = copy.deepcopy(self)
|
67
|
-
question.question_options = list(
|
68
|
-
random.sample(self.question_options, len(self.question_options))
|
69
|
-
)
|
70
|
-
return question
|
71
|
-
|
72
42
|
def loop(self, scenario_list: ScenarioList) -> List[QuestionBase]:
|
73
43
|
"""Return a list of questions with the question name modified for each scenario.
|
74
44
|
|
@@ -80,22 +50,57 @@ class QuestionBaseGenMixin:
|
|
80
50
|
>>> len(q.loop(ScenarioList.from_list("subject", ["Math", "Economics", "Chemistry"])))
|
81
51
|
3
|
82
52
|
"""
|
83
|
-
from edsl.questions.
|
53
|
+
from edsl.questions.LoopProcessor import LoopProcessor
|
84
54
|
|
85
55
|
lp = LoopProcessor(self)
|
86
56
|
return lp.process_templates(scenario_list)
|
87
57
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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":
|
103
|
+
"""Render the question components as jinja2 templates with the replacement dictionary."""
|
99
104
|
from jinja2 import Environment
|
100
105
|
from edsl.scenarios.Scenario import Scenario
|
101
106
|
|
@@ -122,23 +127,15 @@ class QuestionBaseGenMixin:
|
|
122
127
|
|
123
128
|
return self.apply_function(render_string)
|
124
129
|
|
125
|
-
def apply_function(
|
126
|
-
self, func: Callable, exclude_components: List[str] = None
|
127
|
-
) -> QuestionBase:
|
130
|
+
def apply_function(self, func: Callable, exclude_components=None) -> QuestionBase:
|
128
131
|
"""Apply a function to the question parts
|
129
132
|
|
130
|
-
:param func: The function to apply to the question parts.
|
131
|
-
:param exclude_components: The components to exclude from the function application.
|
132
|
-
|
133
133
|
>>> from edsl.questions import QuestionFreeText
|
134
134
|
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
|
135
135
|
>>> shouting = lambda x: x.upper()
|
136
136
|
>>> q.apply_function(shouting)
|
137
137
|
Question('free_text', question_name = \"""color\""", question_text = \"""WHAT IS YOUR FAVORITE COLOR?\""")
|
138
138
|
|
139
|
-
>>> q.apply_function(shouting, exclude_components = ["question_type"])
|
140
|
-
Question('free_text', question_name = \"""COLOR\""", question_text = \"""WHAT IS YOUR FAVORITE COLOR?\""")
|
141
|
-
|
142
139
|
"""
|
143
140
|
from edsl.questions.QuestionBase import QuestionBase
|
144
141
|
|
@@ -69,7 +69,7 @@ class QuestionBasePromptsMixin:
|
|
69
69
|
>>> q.get_instructions(model = "gpt3")
|
70
70
|
Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
|
71
71
|
"""
|
72
|
-
from edsl.language_models.
|
72
|
+
from edsl.language_models.registry import Model
|
73
73
|
|
74
74
|
if not hasattr(self, "_model_instructions"):
|
75
75
|
self._model_instructions = {}
|
edsl/questions/QuestionBudget.py
CHANGED
@@ -5,7 +5,7 @@ from pydantic import Field, BaseModel, validator
|
|
5
5
|
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
7
7
|
from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
|
8
|
-
from edsl.questions.
|
8
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
9
9
|
|
10
10
|
|
11
11
|
class BudgewResponseValidator(ResponseValidatorABC):
|
@@ -13,8 +13,8 @@ from edsl.questions.descriptors import (
|
|
13
13
|
from edsl.questions.decorators import inject_exception
|
14
14
|
|
15
15
|
from pydantic import field_validator
|
16
|
-
from edsl.questions.
|
17
|
-
from edsl.questions.
|
16
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
17
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
18
18
|
|
19
19
|
from edsl.exceptions.questions import QuestionAnswerValidationError
|
20
20
|
|
@@ -6,8 +6,9 @@ from typing import Any, Optional, Dict
|
|
6
6
|
from edsl.questions.QuestionBase import QuestionBase
|
7
7
|
from edsl.questions.descriptors import AnswerTemplateDescriptor
|
8
8
|
|
9
|
-
from edsl.questions.
|
10
|
-
from edsl.questions.
|
9
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
10
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
11
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
11
12
|
from edsl.questions.decorators import inject_exception
|
12
13
|
|
13
14
|
from typing import Dict, Any
|
@@ -56,7 +57,7 @@ def dict_to_pydantic_model(input_dict: Dict[str, Any]) -> Any:
|
|
56
57
|
DynamicModel = create_model("DynamicModel", **field_definitions)
|
57
58
|
|
58
59
|
class AnswerModel(BaseResponse):
|
59
|
-
answer:
|
60
|
+
answer: DynamicModel
|
60
61
|
generated_tokens: Optional[str] = None
|
61
62
|
comment: Optional[str] = None
|
62
63
|
|
@@ -112,8 +113,6 @@ class QuestionExtract(QuestionBase):
|
|
112
113
|
:param question_name: The name of the question.
|
113
114
|
:param question_text: The text of the question.
|
114
115
|
:param answer_template: The template for the answer.
|
115
|
-
:param answering_instructions: Instructions for answering the question.
|
116
|
-
:param question_presentation: The presentation of the question.
|
117
116
|
"""
|
118
117
|
self.question_name = question_name
|
119
118
|
self.question_text = question_text
|
@@ -143,6 +142,9 @@ class QuestionExtract(QuestionBase):
|
|
143
142
|
)
|
144
143
|
return question_html_content
|
145
144
|
|
145
|
+
################
|
146
|
+
# Helpful methods
|
147
|
+
################
|
146
148
|
@classmethod
|
147
149
|
@inject_exception
|
148
150
|
def example(cls) -> QuestionExtract:
|
@@ -5,7 +5,7 @@ from uuid import uuid4
|
|
5
5
|
from pydantic import field_validator
|
6
6
|
|
7
7
|
from edsl.questions.QuestionBase import QuestionBase
|
8
|
-
from edsl.questions.
|
8
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
9
9
|
|
10
10
|
from edsl.exceptions.questions import QuestionAnswerValidationError
|
11
11
|
from edsl.questions.decorators import inject_exception
|
edsl/questions/QuestionList.py
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import json
|
3
|
-
from typing import Any, Optional, Union
|
4
|
-
|
5
|
-
from pydantic import Field
|
6
|
-
from json_repair import repair_json
|
7
3
|
|
8
|
-
from
|
4
|
+
from typing import Any, Optional, Union
|
9
5
|
from edsl.questions.QuestionBase import QuestionBase
|
10
6
|
from edsl.questions.descriptors import IntegerOrNoneDescriptor
|
11
7
|
from edsl.questions.decorators import inject_exception
|
12
|
-
from edsl.questions.response_validator_abc import ResponseValidatorABC
|
13
8
|
|
9
|
+
from pydantic import field_validator, Field
|
10
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
11
|
+
from edsl.questions.ResponseValidatorABC import BaseResponse
|
12
|
+
|
13
|
+
from edsl.exceptions.questions import QuestionAnswerValidationError
|
14
|
+
|
15
|
+
from json_repair import repair_json
|
14
16
|
|
15
|
-
|
17
|
+
|
18
|
+
def convert_string(s):
|
16
19
|
"""Convert a string to a more appropriate type if possible.
|
17
20
|
|
18
21
|
>>> convert_string("3.14")
|
@@ -55,7 +58,7 @@ def convert_string(s: str) -> Union[float, int, str, dict]:
|
|
55
58
|
return s
|
56
59
|
|
57
60
|
|
58
|
-
def create_model(max_list_items: int, permissive
|
61
|
+
def create_model(max_list_items: int, permissive):
|
59
62
|
from pydantic import BaseModel
|
60
63
|
|
61
64
|
if permissive or max_list_items is None:
|
@@ -130,8 +133,8 @@ class QuestionList(QuestionBase):
|
|
130
133
|
self,
|
131
134
|
question_name: str,
|
132
135
|
question_text: str,
|
133
|
-
include_comment: bool = True,
|
134
136
|
max_list_items: Optional[int] = None,
|
137
|
+
include_comment: bool = True,
|
135
138
|
answering_instructions: Optional[str] = None,
|
136
139
|
question_presentation: Optional[str] = None,
|
137
140
|
permissive: bool = False,
|
@@ -181,6 +184,9 @@ class QuestionList(QuestionBase):
|
|
181
184
|
).render(question_name=self.question_name)
|
182
185
|
return question_html_content
|
183
186
|
|
187
|
+
################
|
188
|
+
# Helpful methods
|
189
|
+
################
|
184
190
|
@classmethod
|
185
191
|
@inject_exception
|
186
192
|
def example(
|
edsl/questions/QuestionMatrix.py
CHANGED
@@ -10,7 +10,7 @@ from edsl.questions.descriptors import (
|
|
10
10
|
OptionLabelDescriptor,
|
11
11
|
QuestionTextDescriptor,
|
12
12
|
)
|
13
|
-
from edsl.questions.
|
13
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
14
14
|
from edsl.questions.decorators import inject_exception
|
15
15
|
from edsl.exceptions.questions import (
|
16
16
|
QuestionAnswerValidationError,
|
@@ -8,7 +8,7 @@ from edsl.scenarios.Scenario import Scenario
|
|
8
8
|
from edsl.questions.QuestionBase import QuestionBase
|
9
9
|
from edsl.questions.descriptors import QuestionOptionsDescriptor
|
10
10
|
from edsl.questions.decorators import inject_exception
|
11
|
-
from edsl.questions.
|
11
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
12
12
|
|
13
13
|
|
14
14
|
def create_response_model(choices: List[str], permissive: bool = False):
|
@@ -9,7 +9,7 @@ from edsl.exceptions.questions import QuestionAnswerValidationError
|
|
9
9
|
from edsl.questions.QuestionBase import QuestionBase
|
10
10
|
from edsl.questions.descriptors import NumericalOrNoneDescriptor
|
11
11
|
from edsl.questions.decorators import inject_exception
|
12
|
-
from edsl.questions.
|
12
|
+
from edsl.questions.ResponseValidatorABC import ResponseValidatorABC
|
13
13
|
|
14
14
|
|
15
15
|
def create_numeric_response(
|