edsl 0.1.29.dev6__py3-none-any.whl → 0.1.30.dev1__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.
Files changed (60) hide show
  1. edsl/Base.py +6 -3
  2. edsl/__init__.py +23 -23
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +35 -34
  5. edsl/agents/AgentList.py +16 -5
  6. edsl/agents/Invigilator.py +19 -1
  7. edsl/agents/descriptors.py +2 -1
  8. edsl/base/Base.py +289 -0
  9. edsl/config.py +2 -1
  10. edsl/coop/utils.py +28 -1
  11. edsl/data/Cache.py +19 -5
  12. edsl/data/SQLiteDict.py +11 -3
  13. edsl/jobs/Answers.py +15 -1
  14. edsl/jobs/Jobs.py +69 -31
  15. edsl/jobs/buckets/ModelBuckets.py +4 -2
  16. edsl/jobs/buckets/TokenBucket.py +1 -2
  17. edsl/jobs/interviews/Interview.py +0 -6
  18. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +9 -5
  19. edsl/jobs/runners/JobsRunnerAsyncio.py +12 -16
  20. edsl/jobs/tasks/TaskHistory.py +4 -3
  21. edsl/language_models/LanguageModel.py +5 -11
  22. edsl/language_models/ModelList.py +1 -1
  23. edsl/language_models/repair.py +8 -7
  24. edsl/notebooks/Notebook.py +9 -3
  25. edsl/questions/QuestionBase.py +6 -2
  26. edsl/questions/QuestionBudget.py +5 -6
  27. edsl/questions/QuestionCheckBox.py +7 -3
  28. edsl/questions/QuestionExtract.py +5 -3
  29. edsl/questions/QuestionFreeText.py +3 -3
  30. edsl/questions/QuestionFunctional.py +0 -3
  31. edsl/questions/QuestionList.py +3 -4
  32. edsl/questions/QuestionMultipleChoice.py +12 -5
  33. edsl/questions/QuestionNumerical.py +4 -3
  34. edsl/questions/QuestionRank.py +5 -3
  35. edsl/questions/__init__.py +4 -3
  36. edsl/questions/descriptors.py +4 -2
  37. edsl/results/DatasetExportMixin.py +491 -0
  38. edsl/results/Result.py +13 -65
  39. edsl/results/Results.py +91 -39
  40. edsl/results/ResultsDBMixin.py +7 -3
  41. edsl/results/ResultsExportMixin.py +22 -537
  42. edsl/results/ResultsGGMixin.py +3 -3
  43. edsl/results/ResultsToolsMixin.py +1 -4
  44. edsl/scenarios/FileStore.py +140 -0
  45. edsl/scenarios/Scenario.py +5 -6
  46. edsl/scenarios/ScenarioList.py +17 -8
  47. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  48. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  49. edsl/scenarios/__init__.py +1 -0
  50. edsl/surveys/MemoryPlan.py +11 -4
  51. edsl/surveys/Survey.py +9 -4
  52. edsl/surveys/SurveyExportMixin.py +4 -2
  53. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  54. edsl/utilities/__init__.py +21 -21
  55. edsl/utilities/interface.py +66 -45
  56. edsl/utilities/utilities.py +11 -13
  57. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/METADATA +1 -1
  58. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/RECORD +60 -56
  59. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/LICENSE +0 -0
  60. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/WHEEL +0 -0
@@ -7,26 +7,18 @@ import asyncio
7
7
  import json
8
8
  import time
9
9
  import os
10
-
11
10
  from typing import Coroutine, Any, Callable, Type, List, get_type_hints
12
-
13
- from abc import ABC, abstractmethod, ABCMeta
14
-
15
- from rich.table import Table
11
+ from abc import ABC, abstractmethod
16
12
 
17
13
  from edsl.config import CONFIG
18
14
 
19
- from edsl.utilities.utilities import clean_json
20
15
  from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
21
16
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
17
+
22
18
  from edsl.language_models.repair import repair
23
- from edsl.exceptions.language_models import LanguageModelAttributeTypeError
24
19
  from edsl.enums import InferenceServiceType
25
20
  from edsl.Base import RichPrintingMixin, PersistenceMixin
26
- from edsl.data.Cache import Cache
27
21
  from edsl.enums import service_to_api_keyname
28
-
29
-
30
22
  from edsl.exceptions import MissingAPIKeyError
31
23
  from edsl.language_models.RegisterLanguageModelsMeta import RegisterLanguageModelsMeta
32
24
 
@@ -291,7 +283,7 @@ class LanguageModel(
291
283
  self,
292
284
  user_prompt: str,
293
285
  system_prompt: str,
294
- cache,
286
+ cache: "Cache",
295
287
  iteration: int = 0,
296
288
  encoded_image=None,
297
289
  ) -> tuple[dict, bool, str]:
@@ -490,6 +482,8 @@ class LanguageModel(
490
482
 
491
483
  def rich_print(self):
492
484
  """Display an object as a table."""
485
+ from rich.table import Table
486
+
493
487
  table = Table(title="Language Model")
494
488
  table.add_column("Attribute", style="bold")
495
489
  table.add_column("Value")
@@ -5,7 +5,7 @@ from edsl import Model
5
5
  from edsl.language_models import LanguageModel
6
6
  from edsl.Base import Base
7
7
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
8
- from edsl.utilities import is_valid_variable_name
8
+ from edsl.utilities.utilities import is_valid_variable_name
9
9
  from edsl.utilities.utilities import dict_hash
10
10
 
11
11
 
@@ -1,18 +1,13 @@
1
1
  import json
2
2
  import asyncio
3
3
  import warnings
4
- from rich import print
5
- from rich.console import Console
6
- from rich.syntax import Syntax
7
-
8
- from edsl.utilities.utilities import clean_json
9
-
10
- from edsl.utilities.repair_functions import extract_json_from_string
11
4
 
12
5
 
13
6
  async def async_repair(
14
7
  bad_json, error_message="", user_prompt=None, system_prompt=None, cache=None
15
8
  ):
9
+ from edsl.utilities.utilities import clean_json
10
+
16
11
  s = clean_json(bad_json)
17
12
 
18
13
  try:
@@ -27,6 +22,8 @@ async def async_repair(
27
22
  return valid_dict, success
28
23
 
29
24
  try:
25
+ from edsl.utilities.repair_functions import extract_json_from_string
26
+
30
27
  valid_dict = extract_json_from_string(s)
31
28
  success = True
32
29
  except ValueError:
@@ -98,6 +95,10 @@ async def async_repair(
98
95
  except json.JSONDecodeError:
99
96
  valid_dict = {}
100
97
  success = False
98
+ from rich import print
99
+ from rich.console import Console
100
+ from rich.syntax import Syntax
101
+
101
102
  console = Console()
102
103
  error_message = (
103
104
  f"All repairs. failed. LLM Model given [red]{str(bad_json)}[/red]"
@@ -1,10 +1,9 @@
1
1
  """A Notebook is a utility class that allows you to easily share/pull ipynbs from Coop."""
2
2
 
3
3
  import json
4
- import nbformat
5
- from nbconvert import HTMLExporter
6
4
  from typing import Dict, List, Optional
7
- from rich.table import Table
5
+
6
+
8
7
  from edsl.Base import Base
9
8
  from edsl.utilities.decorators import (
10
9
  add_edsl_version,
@@ -34,6 +33,8 @@ class Notebook(Base):
34
33
  If no path is provided, assume this code is run in a notebook and try to load the current notebook from file.
35
34
  :param name: A name for the Notebook.
36
35
  """
36
+ import nbformat
37
+
37
38
  # Load current notebook path as fallback (VS Code only)
38
39
  path = path or globals().get("__vsc_ipynb_file__")
39
40
  if data is not None:
@@ -134,6 +135,9 @@ class Notebook(Base):
134
135
  """
135
136
  Return HTML representation of Notebook.
136
137
  """
138
+ from nbconvert import HTMLExporter
139
+ import nbformat
140
+
137
141
  notebook = nbformat.from_dict(self.data)
138
142
  html_exporter = HTMLExporter(template_name="basic")
139
143
  (body, _) = html_exporter.from_notebook_node(notebook)
@@ -174,6 +178,8 @@ class Notebook(Base):
174
178
  """
175
179
  Display a Notebook as a rich table.
176
180
  """
181
+ from rich.table import Table
182
+
177
183
  table_data, column_names = self._table()
178
184
  table = Table(title=f"{self.__class__.__name__} Attributes")
179
185
  for column in column_names:
@@ -1,8 +1,8 @@
1
1
  """This module contains the Question class, which is the base class for all questions in EDSL."""
2
2
 
3
3
  from __future__ import annotations
4
+ import time
4
5
  from abc import ABC, abstractmethod
5
- from rich.table import Table
6
6
  from typing import Any, Type, Optional, List, Callable
7
7
  import copy
8
8
 
@@ -12,7 +12,7 @@ from edsl.exceptions import (
12
12
  )
13
13
  from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
14
14
 
15
- from edsl.prompts.registry import get_classes as prompt_lookup
15
+
16
16
  from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
17
17
  from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
18
18
  from edsl.Base import PersistenceMixin, RichPrintingMixin
@@ -124,6 +124,8 @@ class QuestionBase(
124
124
  :param model: The language model to use. If None, assumes does not matter.
125
125
 
126
126
  """
127
+ from edsl.prompts.registry import get_classes as prompt_lookup
128
+
127
129
  applicable_prompts = prompt_lookup(
128
130
  component_type="question_instructions",
129
131
  question_type=cls.question_type,
@@ -496,6 +498,8 @@ class QuestionBase(
496
498
 
497
499
  def rich_print(self):
498
500
  """Print the question in a rich format."""
501
+ from rich.table import Table
502
+
499
503
  table = Table(show_header=True, header_style="bold magenta")
500
504
  table.add_column("Question Name", style="dim")
501
505
  table.add_column("Question Type")
@@ -1,11 +1,8 @@
1
1
  from __future__ import annotations
2
2
  import random
3
- import textwrap
4
3
  from typing import Any, Optional, Union
5
4
  from edsl.questions.QuestionBase import QuestionBase
6
5
  from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
7
- from edsl.scenarios import Scenario
8
- from edsl.utilities import random_string
9
6
 
10
7
 
11
8
  class QuestionBudget(QuestionBase):
@@ -46,7 +43,7 @@ class QuestionBudget(QuestionBase):
46
43
  return answer
47
44
 
48
45
  def _translate_answer_code_to_answer(
49
- self, answer_codes: dict[str, int], scenario: Scenario = None
46
+ self, answer_codes: dict[str, int], scenario: "Scenario" = None
50
47
  ):
51
48
  """
52
49
  Translate the answer codes to the actual answers.
@@ -63,6 +60,8 @@ class QuestionBudget(QuestionBase):
63
60
 
64
61
  def _simulate_answer(self, human_readable=True):
65
62
  """Simulate a valid answer for debugging purposes (what the validator expects)."""
63
+ from edsl.utilities.utilities import random_string
64
+
66
65
  if human_readable:
67
66
  keys = self.question_options
68
67
  else:
@@ -163,8 +162,8 @@ def main():
163
162
 
164
163
 
165
164
  if __name__ == "__main__":
166
- q = QuestionBudget.example()
167
- results = q.run()
165
+ # q = QuestionBudget.example()
166
+ # results = q.run()
168
167
 
169
168
  import doctest
170
169
 
@@ -9,8 +9,6 @@ from edsl.questions.descriptors import (
9
9
  IntegerDescriptor,
10
10
  QuestionOptionsDescriptor,
11
11
  )
12
- from edsl.scenarios import Scenario
13
- from edsl.utilities import random_string
14
12
 
15
13
 
16
14
  class QuestionCheckBox(QuestionBase):
@@ -55,13 +53,17 @@ class QuestionCheckBox(QuestionBase):
55
53
  self._validate_answer_checkbox(answer)
56
54
  return answer
57
55
 
58
- def _translate_answer_code_to_answer(self, answer_codes, scenario: Scenario = None):
56
+ def _translate_answer_code_to_answer(
57
+ self, answer_codes, scenario: "Scenario" = None
58
+ ):
59
59
  """
60
60
  Translate the answer code to the actual answer.
61
61
 
62
62
  For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
63
63
  The LLM will respond with [0,1] and this code will translate it to ["a","b"].
64
64
  """
65
+ from edsl.scenarios.Scenario import Scenario
66
+
65
67
  scenario = scenario or Scenario()
66
68
  translated_options = [
67
69
  Template(option).render(scenario) for option in self.question_options
@@ -73,6 +75,8 @@ class QuestionCheckBox(QuestionBase):
73
75
 
74
76
  def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
75
77
  """Simulate a valid answer for debugging purposes."""
78
+ from edsl.utilities.utilities import random_string
79
+
76
80
  min_selections = self.min_selections or 1
77
81
  max_selections = self.max_selections or len(self.question_options)
78
82
  num_selections = random.randint(min_selections, max_selections)
@@ -2,8 +2,6 @@ from __future__ import annotations
2
2
  from typing import Any
3
3
  from edsl.questions.QuestionBase import QuestionBase
4
4
  from edsl.questions.descriptors import AnswerTemplateDescriptor
5
- from edsl.scenarios import Scenario
6
- from edsl.utilities import random_string
7
5
 
8
6
 
9
7
  class QuestionExtract(QuestionBase):
@@ -44,12 +42,14 @@ class QuestionExtract(QuestionBase):
44
42
  self._validate_answer_extract(answer)
45
43
  return answer
46
44
 
47
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
45
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
48
46
  """Return the answer in a human-readable format."""
49
47
  return answer
50
48
 
51
49
  def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
52
50
  """Simulate a valid answer for debugging purposes."""
51
+ from edsl.utilities.utilities import random_string
52
+
53
53
  return {
54
54
  "answer": {key: random_string() for key in self.answer_template.keys()},
55
55
  "comment": random_string(),
@@ -106,6 +106,8 @@ def main():
106
106
  q.to_dict()
107
107
  assert q.from_dict(q.to_dict()) == q
108
108
 
109
+
110
+ if __name__ == "__main__":
109
111
  import doctest
110
112
 
111
113
  doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -2,8 +2,6 @@ from __future__ import annotations
2
2
  import textwrap
3
3
  from typing import Any, Optional
4
4
  from edsl.questions.QuestionBase import QuestionBase
5
- from edsl.scenarios import Scenario
6
- from edsl.utilities import random_string
7
5
 
8
6
 
9
7
  class QuestionFreeText(QuestionBase):
@@ -43,12 +41,14 @@ class QuestionFreeText(QuestionBase):
43
41
  self._validate_answer_key_value(answer, "answer", str)
44
42
  return answer
45
43
 
46
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
44
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
47
45
  """Do nothing, because the answer is already in a human-readable format."""
48
46
  return answer
49
47
 
50
48
  def _simulate_answer(self, human_readable: bool = True) -> dict[str, str]:
51
49
  """Simulate a valid answer for debugging purposes."""
50
+ from edsl.utilities.utilities import random_string
51
+
52
52
  return {"answer": random_string()}
53
53
 
54
54
  @property
@@ -1,10 +1,7 @@
1
1
  from typing import Optional, Callable
2
- from edsl.questions.QuestionBase import QuestionBase
3
- from edsl.questions.descriptors import FunctionDescriptor
4
2
  import inspect
5
3
 
6
4
  from edsl.questions.QuestionBase import QuestionBase
7
- from edsl.questions.descriptors import FunctionDescriptor
8
5
 
9
6
  from edsl.utilities.restricted_python import create_restricted_function
10
7
 
@@ -5,9 +5,6 @@ from typing import Any, Optional, Union
5
5
  from edsl.questions.QuestionBase import QuestionBase
6
6
  from edsl.questions.descriptors import IntegerOrNoneDescriptor
7
7
 
8
- from edsl.scenarios import Scenario
9
- from edsl.utilities import random_string
10
-
11
8
 
12
9
  class QuestionList(QuestionBase):
13
10
  """This question prompts the agent to answer by providing a list of items as comma-separated strings."""
@@ -42,13 +39,15 @@ class QuestionList(QuestionBase):
42
39
  self._validate_answer_list(answer)
43
40
  return answer
44
41
 
45
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
42
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
46
43
  """There is no answer code."""
47
44
  return answer
48
45
 
49
46
  def _simulate_answer(self, human_readable: bool = True):
50
47
  """Simulate a valid answer for debugging purposes (what the validator expects)."""
51
48
  num_items = random.randint(1, self.max_list_items or 2)
49
+ from edsl.utilities.utilities import random_string
50
+
52
51
  return {"answer": [random_string() for _ in range(num_items)]}
53
52
 
54
53
  @property
@@ -1,13 +1,12 @@
1
1
  from __future__ import annotations
2
- from typing import Optional, Union
2
+ import time
3
+ from typing import Union
3
4
  import random
4
5
 
5
6
  from jinja2 import Template
6
7
 
7
- from edsl.utilities import random_string
8
- from edsl.questions.descriptors import QuestionOptionsDescriptor
9
8
  from edsl.questions.QuestionBase import QuestionBase
10
- from edsl.scenarios import Scenario
9
+ from edsl.questions.descriptors import QuestionOptionsDescriptor
11
10
 
12
11
 
13
12
  class QuestionMultipleChoice(QuestionBase):
@@ -47,8 +46,12 @@ class QuestionMultipleChoice(QuestionBase):
47
46
  self._validate_answer_multiple_choice(answer)
48
47
  return answer
49
48
 
50
- def _translate_answer_code_to_answer(self, answer_code, scenario: Scenario = None):
49
+ def _translate_answer_code_to_answer(
50
+ self, answer_code, scenario: "Scenario" = None
51
+ ):
51
52
  """Translate the answer code to the actual answer."""
53
+ from edsl.scenarios.Scenario import Scenario
54
+
52
55
  scenario = scenario or Scenario()
53
56
  translated_options = [
54
57
  Template(str(option)).render(scenario) for option in self.question_options
@@ -59,6 +62,8 @@ class QuestionMultipleChoice(QuestionBase):
59
62
  self, human_readable: bool = True
60
63
  ) -> dict[str, Union[int, str]]:
61
64
  """Simulate a valid answer for debugging purposes."""
65
+ from edsl.utilities.utilities import random_string
66
+
62
67
  if human_readable:
63
68
  answer = random.choice(self.question_options)
64
69
  else:
@@ -127,6 +132,8 @@ def main():
127
132
  q.to_dict()
128
133
  assert q.from_dict(q.to_dict()) == q
129
134
 
135
+
136
+ if __name__ == "__main__":
130
137
  import doctest
131
138
 
132
139
  doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -2,11 +2,10 @@ from __future__ import annotations
2
2
  import textwrap
3
3
  from random import uniform
4
4
  from typing import Any, Optional, Union
5
+
5
6
  from edsl.exceptions import QuestionAnswerValidationError
6
7
  from edsl.questions.QuestionBase import QuestionBase
7
8
  from edsl.questions.descriptors import NumericalOrNoneDescriptor
8
- from edsl.scenarios import Scenario
9
- from edsl.utilities import random_string
10
9
 
11
10
 
12
11
  class QuestionNumerical(QuestionBase):
@@ -48,12 +47,14 @@ class QuestionNumerical(QuestionBase):
48
47
  self._validate_answer_numerical(answer)
49
48
  return answer
50
49
 
51
- def _translate_answer_code_to_answer(self, answer, scenario: Scenario = None):
50
+ def _translate_answer_code_to_answer(self, answer, scenario: "Scenario" = None):
52
51
  """There is no answer code."""
53
52
  return answer
54
53
 
55
54
  def _simulate_answer(self, human_readable: bool = True):
56
55
  """Simulate a valid answer for debugging purposes."""
56
+ from edsl.utilities.utilities import random_string
57
+
57
58
  return {
58
59
  "answer": uniform(self.min_value, self.max_value),
59
60
  "comment": random_string(),
@@ -1,12 +1,10 @@
1
1
  from __future__ import annotations
2
2
  import random
3
- import textwrap
4
3
  from jinja2 import Template
5
4
  from typing import Any, Optional, Union
6
5
  from edsl.questions.QuestionBase import QuestionBase
7
6
  from edsl.exceptions import QuestionAnswerValidationError
8
- from edsl.scenarios import Scenario
9
- from edsl.utilities.utilities import random_string
7
+
10
8
  from edsl.questions.descriptors import (
11
9
  QuestionOptionsDescriptor,
12
10
  NumSelectionsDescriptor,
@@ -55,6 +53,8 @@ class QuestionRank(QuestionBase):
55
53
  self, answer_codes, scenario: Scenario = None
56
54
  ) -> list[str]:
57
55
  """Translate the answer code to the actual answer."""
56
+ from edsl.scenarios import Scenario
57
+
58
58
  scenario = scenario or Scenario()
59
59
  translated_options = [
60
60
  Template(option).render(scenario) for option in self.question_options
@@ -66,6 +66,8 @@ class QuestionRank(QuestionBase):
66
66
 
67
67
  def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
68
68
  """Simulate a valid answer for debugging purposes."""
69
+ from edsl.utilities.utilities import random_string
70
+
69
71
  if human_readable:
70
72
  selected = random.sample(self.question_options, self.num_selections)
71
73
  else:
@@ -10,17 +10,18 @@ from edsl.questions.QuestionBudget import QuestionBudget
10
10
  from edsl.questions.QuestionCheckBox import QuestionCheckBox
11
11
  from edsl.questions.QuestionExtract import QuestionExtract
12
12
  from edsl.questions.QuestionFreeText import QuestionFreeText
13
+
13
14
  from edsl.questions.QuestionFunctional import QuestionFunctional
14
15
  from edsl.questions.QuestionList import QuestionList
15
16
  from edsl.questions.QuestionMultipleChoice import QuestionMultipleChoice
16
17
  from edsl.questions.QuestionNumerical import QuestionNumerical
17
18
  from edsl.questions.QuestionRank import QuestionRank
18
19
 
19
- # Questions derived from core questions
20
+ # # Questions derived from core questions
20
21
  from edsl.questions.derived.QuestionLikertFive import QuestionLikertFive
21
22
  from edsl.questions.derived.QuestionLinearScale import QuestionLinearScale
22
23
  from edsl.questions.derived.QuestionTopK import QuestionTopK
23
24
  from edsl.questions.derived.QuestionYesNo import QuestionYesNo
24
25
 
25
- # Compose Questions
26
- from edsl.questions.compose_questions import compose_questions
26
+ # # Compose Questions
27
+ # from edsl.questions.compose_questions import compose_questions
@@ -8,9 +8,7 @@ from edsl.exceptions import (
8
8
  QuestionAnswerValidationError,
9
9
  )
10
10
  from edsl.questions.settings import Settings
11
- from edsl.utilities.utilities import is_valid_variable_name
12
11
 
13
- from edsl.prompts import get_classes
14
12
 
15
13
  ################################
16
14
  # Helper functions
@@ -56,6 +54,8 @@ class BaseDescriptor(ABC):
56
54
  def __set__(self, instance, value: Any) -> None:
57
55
  """Set the value of the attribute."""
58
56
  self.validate(value, instance)
57
+ from edsl.prompts.registry import get_classes
58
+
59
59
  instance.__dict__[self.name] = value
60
60
  if self.name == "_instructions":
61
61
  instructions = value
@@ -231,6 +231,8 @@ class QuestionNameDescriptor(BaseDescriptor):
231
231
 
232
232
  def validate(self, value, instance):
233
233
  """Validate the value is a valid variable name."""
234
+ from edsl.utilities.utilities import is_valid_variable_name
235
+
234
236
  if not is_valid_variable_name(value):
235
237
  raise QuestionCreationValidationError(
236
238
  f"`question_name` is not a valid variable name (got {value})."