edsl 0.1.28__py3-none-any.whl → 0.1.29__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 (76) hide show
  1. edsl/Base.py +18 -18
  2. edsl/__init__.py +24 -24
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +77 -41
  5. edsl/agents/AgentList.py +35 -6
  6. edsl/agents/Invigilator.py +19 -1
  7. edsl/agents/InvigilatorBase.py +15 -10
  8. edsl/agents/PromptConstructionMixin.py +342 -100
  9. edsl/agents/descriptors.py +2 -1
  10. edsl/base/Base.py +289 -0
  11. edsl/config.py +2 -1
  12. edsl/conjure/InputData.py +39 -8
  13. edsl/coop/coop.py +188 -151
  14. edsl/coop/utils.py +43 -75
  15. edsl/data/Cache.py +19 -5
  16. edsl/data/SQLiteDict.py +11 -3
  17. edsl/jobs/Answers.py +15 -1
  18. edsl/jobs/Jobs.py +92 -47
  19. edsl/jobs/buckets/ModelBuckets.py +4 -2
  20. edsl/jobs/buckets/TokenBucket.py +1 -2
  21. edsl/jobs/interviews/Interview.py +3 -9
  22. edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
  23. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +15 -10
  24. edsl/jobs/runners/JobsRunnerAsyncio.py +21 -25
  25. edsl/jobs/tasks/TaskHistory.py +4 -3
  26. edsl/language_models/LanguageModel.py +5 -11
  27. edsl/language_models/ModelList.py +3 -3
  28. edsl/language_models/repair.py +8 -7
  29. edsl/notebooks/Notebook.py +40 -3
  30. edsl/prompts/Prompt.py +31 -19
  31. edsl/questions/QuestionBase.py +38 -13
  32. edsl/questions/QuestionBudget.py +5 -6
  33. edsl/questions/QuestionCheckBox.py +7 -3
  34. edsl/questions/QuestionExtract.py +5 -3
  35. edsl/questions/QuestionFreeText.py +3 -3
  36. edsl/questions/QuestionFunctional.py +0 -3
  37. edsl/questions/QuestionList.py +3 -4
  38. edsl/questions/QuestionMultipleChoice.py +16 -8
  39. edsl/questions/QuestionNumerical.py +4 -3
  40. edsl/questions/QuestionRank.py +5 -3
  41. edsl/questions/__init__.py +4 -3
  42. edsl/questions/descriptors.py +4 -2
  43. edsl/questions/question_registry.py +20 -31
  44. edsl/questions/settings.py +1 -1
  45. edsl/results/Dataset.py +31 -0
  46. edsl/results/DatasetExportMixin.py +493 -0
  47. edsl/results/Result.py +22 -74
  48. edsl/results/Results.py +105 -67
  49. edsl/results/ResultsDBMixin.py +7 -3
  50. edsl/results/ResultsExportMixin.py +22 -537
  51. edsl/results/ResultsGGMixin.py +3 -3
  52. edsl/results/ResultsToolsMixin.py +5 -5
  53. edsl/scenarios/FileStore.py +140 -0
  54. edsl/scenarios/Scenario.py +5 -6
  55. edsl/scenarios/ScenarioList.py +44 -15
  56. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  57. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  58. edsl/scenarios/__init__.py +1 -0
  59. edsl/study/ObjectEntry.py +89 -13
  60. edsl/study/ProofOfWork.py +5 -2
  61. edsl/study/SnapShot.py +4 -8
  62. edsl/study/Study.py +21 -14
  63. edsl/study/__init__.py +2 -0
  64. edsl/surveys/MemoryPlan.py +11 -4
  65. edsl/surveys/Survey.py +46 -7
  66. edsl/surveys/SurveyExportMixin.py +4 -2
  67. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  68. edsl/tools/plotting.py +4 -2
  69. edsl/utilities/__init__.py +21 -21
  70. edsl/utilities/interface.py +66 -45
  71. edsl/utilities/utilities.py +11 -13
  72. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/METADATA +11 -10
  73. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/RECORD +75 -72
  74. edsl-0.1.28.dist-info/entry_points.txt +0 -3
  75. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/LICENSE +0 -0
  76. {edsl-0.1.28.dist-info → edsl-0.1.29.dist-info}/WHEEL +0 -0
edsl/Base.py CHANGED
@@ -6,9 +6,6 @@ import io
6
6
  import json
7
7
  from typing import Any, Optional, Union
8
8
  from uuid import UUID
9
- from IPython.display import display
10
- from rich.console import Console
11
- from edsl.utilities import is_notebook
12
9
 
13
10
 
14
11
  class RichPrintingMixin:
@@ -16,6 +13,8 @@ class RichPrintingMixin:
16
13
 
17
14
  def _for_console(self):
18
15
  """Return a string representation of the object for console printing."""
16
+ from rich.console import Console
17
+
19
18
  with io.StringIO() as buf:
20
19
  console = Console(file=buf, record=True)
21
20
  table = self.rich_print()
@@ -28,7 +27,11 @@ class RichPrintingMixin:
28
27
 
29
28
  def print(self):
30
29
  """Print the object to the console."""
30
+ from edsl.utilities.utilities import is_notebook
31
+
31
32
  if is_notebook():
33
+ from IPython.display import display
34
+
32
35
  display(self.rich_print())
33
36
  else:
34
37
  from rich.console import Console
@@ -52,31 +55,28 @@ class PersistenceMixin:
52
55
  return c.create(self, description, visibility)
53
56
 
54
57
  @classmethod
55
- def pull(cls, id_or_url: Union[str, UUID], exec_profile=None):
58
+ def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
56
59
  """Pull the object from coop."""
57
60
  from edsl.coop import Coop
61
+ from edsl.coop.utils import ObjectRegistry
58
62
 
59
- if id_or_url.startswith("http"):
60
- uuid_value = id_or_url.split("/")[-1]
61
- else:
62
- uuid_value = id_or_url
63
-
64
- c = Coop()
65
-
66
- return c._get_base(cls, uuid_value, exec_profile=exec_profile)
63
+ object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
64
+ coop = Coop()
65
+ return coop.get(uuid, url, object_type)
67
66
 
68
67
  @classmethod
69
- def delete(cls, id_or_url: Union[str, UUID]):
68
+ def delete(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
70
69
  """Delete the object from coop."""
71
70
  from edsl.coop import Coop
72
71
 
73
- c = Coop()
74
- return c._delete_base(cls, id_or_url)
72
+ coop = Coop()
73
+ return coop.delete(uuid, url)
75
74
 
76
75
  @classmethod
77
76
  def patch(
78
77
  cls,
79
- id_or_url: Union[str, UUID],
78
+ uuid: Optional[Union[str, UUID]] = None,
79
+ url: Optional[str] = None,
80
80
  description: Optional[str] = None,
81
81
  value: Optional[Any] = None,
82
82
  visibility: Optional[str] = None,
@@ -89,8 +89,8 @@ class PersistenceMixin:
89
89
  """
90
90
  from edsl.coop import Coop
91
91
 
92
- c = Coop()
93
- return c._patch_base(cls, id_or_url, description, value, visibility)
92
+ coop = Coop()
93
+ return coop.patch(uuid, url, description, value, visibility)
94
94
 
95
95
  @classmethod
96
96
  def search(cls, query):
edsl/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import time
2
3
 
3
4
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
4
5
  ROOT_DIR = os.path.dirname(BASE_DIR)
@@ -7,36 +8,35 @@ from edsl.__version__ import __version__
7
8
  from edsl.config import Config, CONFIG
8
9
  from edsl.agents.Agent import Agent
9
10
  from edsl.agents.AgentList import AgentList
10
- from edsl.questions import (
11
- QuestionBase,
12
- QuestionBudget,
13
- QuestionCheckBox,
14
- QuestionExtract,
15
- QuestionFreeText,
16
- QuestionFunctional,
17
- QuestionLikertFive,
18
- QuestionList,
19
- QuestionLinearScale,
20
- QuestionMultipleChoice,
21
- QuestionNumerical,
22
- QuestionRank,
23
- QuestionTopK,
24
- QuestionYesNo,
25
- )
26
- from edsl.scenarios.Scenario import Scenario
27
- from edsl.scenarios.ScenarioList import ScenarioList
28
- from edsl.utilities.interface import print_dict_with_rich
11
+ from edsl.questions import QuestionBase
12
+ from edsl.questions import QuestionMultipleChoice
13
+ from edsl.questions import QuestionBudget
14
+ from edsl.questions import QuestionCheckBox
15
+ from edsl.questions import QuestionExtract
16
+ from edsl.questions import QuestionFreeText
17
+ from edsl.questions import QuestionFunctional
18
+ from edsl.questions import QuestionLikertFive
19
+ from edsl.questions import QuestionList
20
+ from edsl.questions import QuestionLinearScale
21
+ from edsl.questions import QuestionNumerical
22
+ from edsl.questions import QuestionRank
23
+ from edsl.questions import QuestionTopK
24
+ from edsl.questions import QuestionYesNo
25
+ from edsl.questions.question_registry import Question
26
+ from edsl.scenarios import Scenario
27
+ from edsl.scenarios import ScenarioList
28
+
29
+ # from edsl.utilities.interface import print_dict_with_rich
29
30
  from edsl.surveys.Survey import Survey
30
31
  from edsl.language_models.registry import Model
31
- from edsl.questions.question_registry import Question
32
+ from edsl.language_models.ModelList import ModelList
32
33
  from edsl.results.Results import Results
33
34
  from edsl.data.Cache import Cache
34
35
  from edsl.data.CacheEntry import CacheEntry
35
36
  from edsl.data.CacheHandler import set_session_cache, unset_session_cache
36
37
  from edsl.shared import shared_globals
37
- from edsl.jobs import Jobs
38
- from edsl.notebooks import Notebook
38
+ from edsl.jobs.Jobs import Jobs
39
+ from edsl.notebooks.Notebook import Notebook
39
40
  from edsl.study.Study import Study
40
- from edsl.coop.coop import Coop
41
41
  from edsl.conjure.Conjure import Conjure
42
- from edsl.language_models.ModelList import ModelList
42
+ from edsl.coop.coop import Coop
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.28"
1
+ __version__ = "0.1.29"
edsl/agents/Agent.py CHANGED
@@ -5,27 +5,14 @@ import copy
5
5
  import inspect
6
6
  import types
7
7
  from typing import Any, Callable, Optional, Union, Dict, Sequence
8
-
9
- from rich.table import Table
10
-
11
8
  from edsl.Base import Base
12
- from edsl.questions.QuestionBase import QuestionBase
13
- from edsl.language_models import LanguageModel
14
- from edsl.surveys.MemoryPlan import MemoryPlan
9
+
15
10
  from edsl.exceptions.agents import (
16
11
  AgentCombinationError,
17
12
  AgentDirectAnswerFunctionError,
18
13
  AgentDynamicTraitsFunctionError,
19
14
  )
20
- from edsl.agents.Invigilator import (
21
- InvigilatorDebug,
22
- InvigilatorHuman,
23
- InvigilatorFunctional,
24
- InvigilatorAI,
25
- InvigilatorBase,
26
- )
27
- from edsl.language_models.registry import Model
28
- from edsl.scenarios import Scenario
15
+
29
16
  from edsl.agents.descriptors import (
30
17
  TraitsDescriptor,
31
18
  CodebookDescriptor,
@@ -38,10 +25,6 @@ from edsl.utilities.decorators import (
38
25
  remove_edsl_version,
39
26
  )
40
27
  from edsl.data_transfer_models import AgentResponseDict
41
- from edsl.prompts.library.agent_persona import AgentPersona
42
- from edsl.data.Cache import Cache
43
-
44
-
45
28
  from edsl.utilities.restricted_python import create_restricted_function
46
29
 
47
30
 
@@ -56,6 +39,7 @@ class Agent(Base):
56
39
  name = NameDescriptor()
57
40
  dynamic_traits_function_name = ""
58
41
  answer_question_directly_function_name = ""
42
+ has_dynamic_traits_function = False
59
43
 
60
44
  def __init__(
61
45
  self,
@@ -129,12 +113,16 @@ class Agent(Base):
129
113
 
130
114
  if self.dynamic_traits_function:
131
115
  self.dynamic_traits_function_name = self.dynamic_traits_function.__name__
116
+ self.has_dynamic_traits_function = True
117
+ else:
118
+ self.has_dynamic_traits_function = False
132
119
 
133
120
  if dynamic_traits_function_source_code:
134
121
  self.dynamic_traits_function_name = dynamic_traits_function_name
135
122
  self.dynamic_traits_function = create_restricted_function(
136
123
  dynamic_traits_function_name, dynamic_traits_function
137
124
  )
125
+
138
126
  if answer_question_directly_source_code:
139
127
  self.answer_question_directly_function_name = (
140
128
  answer_question_directly_function_name
@@ -151,6 +139,8 @@ class Agent(Base):
151
139
  self.current_question = None
152
140
 
153
141
  if traits_presentation_template is not None:
142
+ from edsl.prompts.library.agent_persona import AgentPersona
143
+
154
144
  self.traits_presentation_template = traits_presentation_template
155
145
  self.agent_persona = AgentPersona(text=self.traits_presentation_template)
156
146
 
@@ -159,7 +149,7 @@ class Agent(Base):
159
149
 
160
150
  This checks whether the dynamic traits function is valid.
161
151
  """
162
- if self.dynamic_traits_function is not None:
152
+ if self.has_dynamic_traits_function:
163
153
  sig = inspect.signature(self.dynamic_traits_function)
164
154
  if "question" in sig.parameters:
165
155
  if len(sig.parameters) > 1:
@@ -189,7 +179,7 @@ class Agent(Base):
189
179
  {'age': 10, 'hair': 'brown', 'height': 5.5}
190
180
 
191
181
  """
192
- if self.dynamic_traits_function is not None:
182
+ if self.has_dynamic_traits_function:
193
183
  sig = inspect.signature(self.dynamic_traits_function)
194
184
  if "question" in sig.parameters:
195
185
  return self.dynamic_traits_function(question=self.current_question)
@@ -271,8 +261,9 @@ class Agent(Base):
271
261
  def create_invigilator(
272
262
  self,
273
263
  *,
274
- question: QuestionBase,
275
- cache,
264
+ question: "QuestionBase",
265
+ cache: "Cache",
266
+ survey: Optional["Survey"] = None,
276
267
  scenario: Optional[Scenario] = None,
277
268
  model: Optional[LanguageModel] = None,
278
269
  debug: bool = False,
@@ -280,7 +271,7 @@ class Agent(Base):
280
271
  current_answers: Optional[dict] = None,
281
272
  iteration: int = 1,
282
273
  sidecar_model=None,
283
- ) -> InvigilatorBase:
274
+ ) -> "InvigilatorBase":
284
275
  """Create an Invigilator.
285
276
 
286
277
  An invigilator is an object that is responsible for administering a question to an agent.
@@ -294,6 +285,8 @@ class Agent(Base):
294
285
  An invigator is an object that is responsible for administering a question to an agent and
295
286
  recording the responses.
296
287
  """
288
+ from edsl import Model, Scenario
289
+
297
290
  cache = cache
298
291
  self.current_question = question
299
292
  model = model or Model()
@@ -301,6 +294,7 @@ class Agent(Base):
301
294
  invigilator = self._create_invigilator(
302
295
  question=question,
303
296
  scenario=scenario,
297
+ survey=survey,
304
298
  model=model,
305
299
  debug=debug,
306
300
  memory_plan=memory_plan,
@@ -314,12 +308,13 @@ class Agent(Base):
314
308
  async def async_answer_question(
315
309
  self,
316
310
  *,
317
- question: QuestionBase,
318
- cache: Cache,
319
- scenario: Optional[Scenario] = None,
320
- model: Optional[LanguageModel] = None,
311
+ question: "QuestionBase",
312
+ cache: "Cache",
313
+ scenario: Optional["Scenario"] = None,
314
+ survey: Optional["Survey"] = None,
315
+ model: Optional["LanguageModel"] = None,
321
316
  debug: bool = False,
322
- memory_plan: Optional[MemoryPlan] = None,
317
+ memory_plan: Optional["MemoryPlan"] = None,
323
318
  current_answers: Optional[dict] = None,
324
319
  iteration: int = 0,
325
320
  ) -> AgentResponseDict:
@@ -349,6 +344,7 @@ class Agent(Base):
349
344
  question=question,
350
345
  cache=cache,
351
346
  scenario=scenario,
347
+ survey=survey,
352
348
  model=model,
353
349
  debug=debug,
354
350
  memory_plan=memory_plan,
@@ -362,21 +358,35 @@ class Agent(Base):
362
358
 
363
359
  def _create_invigilator(
364
360
  self,
365
- question: QuestionBase,
366
- cache: Optional[Cache] = None,
367
- scenario: Optional[Scenario] = None,
368
- model: Optional[LanguageModel] = None,
361
+ question: "QuestionBase",
362
+ cache: Optional["Cache"] = None,
363
+ scenario: Optional["Scenario"] = None,
364
+ model: Optional["LanguageModel"] = None,
365
+ survey: Optional["Survey"] = None,
369
366
  debug: bool = False,
370
- memory_plan: Optional[MemoryPlan] = None,
367
+ memory_plan: Optional["MemoryPlan"] = None,
371
368
  current_answers: Optional[dict] = None,
372
369
  iteration: int = 0,
373
370
  sidecar_model=None,
374
- ) -> InvigilatorBase:
371
+ ) -> "InvigilatorBase":
375
372
  """Create an Invigilator."""
373
+ from edsl import Model
374
+ from edsl import Scenario
375
+
376
376
  model = model or Model()
377
377
  scenario = scenario or Scenario()
378
378
 
379
+ from edsl.agents.Invigilator import (
380
+ InvigilatorDebug,
381
+ InvigilatorHuman,
382
+ InvigilatorFunctional,
383
+ InvigilatorAI,
384
+ InvigilatorBase,
385
+ )
386
+
379
387
  if cache is None:
388
+ from edsl.data.Cache import Cache
389
+
380
390
  cache = Cache()
381
391
 
382
392
  if debug:
@@ -404,6 +414,7 @@ class Agent(Base):
404
414
  self,
405
415
  question=question,
406
416
  scenario=scenario,
417
+ survey=survey,
407
418
  model=model,
408
419
  memory_plan=memory_plan,
409
420
  current_answers=current_answers,
@@ -479,6 +490,29 @@ class Agent(Base):
479
490
  """
480
491
  return self.data == other.data
481
492
 
493
+ def __getattr__(self, name):
494
+ # This will be called only if 'name' is not found in the usual places
495
+ # breakpoint()
496
+ if name == "has_dynamic_traits_function":
497
+ return self.has_dynamic_traits_function
498
+
499
+ if name in self.traits:
500
+ return self.traits[name]
501
+ raise AttributeError(
502
+ f"'{type(self).__name__}' object has no attribute '{name}'"
503
+ )
504
+
505
+ def __getstate__(self):
506
+ state = self.__dict__.copy()
507
+ # Include any additional state that needs to be serialized
508
+ return state
509
+
510
+ def __setstate__(self, state):
511
+ self.__dict__.update(state)
512
+ # Ensure _traits is initialized if it's missing
513
+ if "_traits" not in self.__dict__:
514
+ self._traits = {}
515
+
482
516
  def print(self) -> None:
483
517
  from rich import print_json
484
518
  import json
@@ -535,9 +569,9 @@ class Agent(Base):
535
569
  if dynamic_traits_func:
536
570
  func = inspect.getsource(dynamic_traits_func)
537
571
  raw_data["dynamic_traits_function_source_code"] = func
538
- raw_data[
539
- "dynamic_traits_function_name"
540
- ] = self.dynamic_traits_function_name
572
+ raw_data["dynamic_traits_function_name"] = (
573
+ self.dynamic_traits_function_name
574
+ )
541
575
  if hasattr(self, "answer_question_directly"):
542
576
  raw_data.pop(
543
577
  "answer_question_directly", None
@@ -553,9 +587,9 @@ class Agent(Base):
553
587
  raw_data["answer_question_directly_source_code"] = inspect.getsource(
554
588
  answer_question_directly_func
555
589
  )
556
- raw_data[
557
- "answer_question_directly_function_name"
558
- ] = self.answer_question_directly_function_name
590
+ raw_data["answer_question_directly_function_name"] = (
591
+ self.answer_question_directly_function_name
592
+ )
559
593
 
560
594
  return raw_data
561
595
 
@@ -640,6 +674,8 @@ class Agent(Base):
640
674
  >>> a.rich_print()
641
675
  <rich.table.Table object at ...>
642
676
  """
677
+ from rich.table import Table
678
+
643
679
  table_data, column_names = self._table()
644
680
  table = Table(title=f"{self.__class__.__name__} Attributes")
645
681
  for column in column_names:
edsl/agents/AgentList.py CHANGED
@@ -12,7 +12,7 @@ Example usage:
12
12
 
13
13
  from __future__ import annotations
14
14
  from collections import UserList
15
- from typing import Optional, Union, Sequence
15
+ from typing import Optional, Union, Sequence, List, Any
16
16
  from rich import print_json
17
17
  from rich.table import Table
18
18
  import json
@@ -22,7 +22,8 @@ import csv
22
22
  from simpleeval import EvalWithCompoundTypes
23
23
 
24
24
  from edsl.Base import Base
25
- from edsl.agents import Agent
25
+
26
+ # from edsl.agents import Agent
26
27
  from edsl.utilities.decorators import (
27
28
  add_edsl_version,
28
29
  remove_edsl_version,
@@ -32,7 +33,7 @@ from edsl.utilities.decorators import (
32
33
  class AgentList(UserList, Base):
33
34
  """A list of Agents."""
34
35
 
35
- def __init__(self, data: Optional[list[Agent]] = None):
36
+ def __init__(self, data: Optional[list["Agent"]] = None):
36
37
  """Initialize a new AgentList.
37
38
 
38
39
  :param data: A list of Agents.
@@ -77,6 +78,7 @@ class AgentList(UserList, Base):
77
78
  def select(self, *traits) -> AgentList:
78
79
  """Selects agents with only the references traits.
79
80
 
81
+ >>> from edsl.agents.Agent import Agent
80
82
  >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
81
83
  >>> al.select('a')
82
84
  AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
@@ -94,12 +96,13 @@ class AgentList(UserList, Base):
94
96
  """
95
97
  Filter a list of agents based on an expression.
96
98
 
99
+ >>> from edsl.agents.Agent import Agent
97
100
  >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
98
101
  >>> al.filter("b == 2")
99
102
  AgentList([Agent(traits = {'a': 1, 'b': 2})])
100
103
  """
101
104
 
102
- def create_evaluator(agent: Agent):
105
+ def create_evaluator(agent: "Agent"):
103
106
  """Create an evaluator for the given result.
104
107
  The 'combined_dict' is a mapping of all values for that Result object.
105
108
  """
@@ -133,6 +136,8 @@ class AgentList(UserList, Base):
133
136
 
134
137
  :param file_path: The path to the CSV file.
135
138
  """
139
+ from edsl.agents.Agent import Agent
140
+
136
141
  agent_list = []
137
142
  with open(file_path, "r") as f:
138
143
  reader = csv.DictReader(f)
@@ -153,7 +158,7 @@ class AgentList(UserList, Base):
153
158
  """Remove traits from the AgentList.
154
159
 
155
160
  :param traits: The traits to remove.
156
-
161
+ >>> from edsl.agents.Agent import Agent
157
162
  >>> al = AgentList([Agent({'age': 22, 'hair': 'brown', 'height': 5.5}), Agent({'age': 22, 'hair': 'brown', 'height': 5.5})])
158
163
  >>> al.remove_trait('age')
159
164
  AgentList([Agent(traits = {'hair': 'brown', 'height': 5.5}), Agent(traits = {'hair': 'brown', 'height': 5.5})])
@@ -222,12 +227,14 @@ class AgentList(UserList, Base):
222
227
  """Deserialize the dictionary back to an AgentList object.
223
228
 
224
229
  :param: data: A dictionary representing an AgentList.
225
-
230
+ >>> from edsl.agents.Agent import Agent
226
231
  >>> al = AgentList([Agent.example(), Agent.example()])
227
232
  >>> al2 = AgentList.from_dict(al.to_dict())
228
233
  >>> al2 == al
229
234
  True
230
235
  """
236
+ from edsl.agents.Agent import Agent
237
+
231
238
  agents = [Agent.from_dict(agent_dict) for agent_dict in data["agent_list"]]
232
239
  return cls(agents)
233
240
 
@@ -240,8 +247,30 @@ class AgentList(UserList, Base):
240
247
  2
241
248
 
242
249
  """
250
+ from edsl.agents.Agent import Agent
251
+
243
252
  return cls([Agent.example(), Agent.example()])
244
253
 
254
+ @classmethod
255
+ def from_list(self, trait_name: str, values: List[Any]):
256
+ """Create an AgentList from a list of values.
257
+
258
+ :param trait_name: The name of the trait.
259
+ :param values: A list of values.
260
+ """
261
+ from edsl.agents.Agent import Agent
262
+
263
+ return AgentList([Agent({trait_name: value}) for value in values])
264
+
265
+ def __mul__(self, other: AgentList) -> AgentList:
266
+ """Takes the cross product of two AgentLists."""
267
+ from itertools import product
268
+
269
+ new_sl = []
270
+ for s1, s2 in list(product(self, other)):
271
+ new_sl.append(s1 + s2)
272
+ return AgentList(new_sl)
273
+
245
274
  def code(self, string=True) -> Union[str, list[str]]:
246
275
  """Return code to construct an AgentList.
247
276
 
@@ -82,7 +82,25 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
82
82
  self._remove_from_cache(raw_response)
83
83
  raise e
84
84
 
85
- answer = question._translate_answer_code_to_answer(response["answer"], scenario)
85
+ # breakpoint()
86
+ question_dict = self.survey.question_names_to_questions()
87
+ for other_question, answer in self.current_answers.items():
88
+ if other_question in question_dict:
89
+ question_dict[other_question].answer = answer
90
+ else:
91
+ # adds a comment to the question
92
+ if (
93
+ new_question := other_question.split("_comment")[0]
94
+ ) in question_dict:
95
+ question_dict[new_question].comment = answer
96
+
97
+ combined_dict = {**question_dict, **scenario}
98
+ # print("combined_dict: ", combined_dict)
99
+ # print("response: ", response)
100
+ # breakpoint()
101
+ answer = question._translate_answer_code_to_answer(
102
+ response["answer"], combined_dict
103
+ )
86
104
  data = {
87
105
  "answer": answer,
88
106
  "comment": response.get(
@@ -46,6 +46,7 @@ class InvigilatorBase(ABC):
46
46
  model: LanguageModel,
47
47
  memory_plan: MemoryPlan,
48
48
  current_answers: dict,
49
+ survey: Optional["Survey"],
49
50
  cache: Optional[Cache] = None,
50
51
  iteration: Optional[int] = 1,
51
52
  additional_prompt_data: Optional[dict] = None,
@@ -57,11 +58,12 @@ class InvigilatorBase(ABC):
57
58
  self.scenario = scenario
58
59
  self.model = model
59
60
  self.memory_plan = memory_plan
60
- self.current_answers = current_answers
61
+ self.current_answers = current_answers or {}
61
62
  self.iteration = iteration
62
63
  self.additional_prompt_data = additional_prompt_data
63
64
  self.cache = cache
64
65
  self.sidecar_model = sidecar_model
66
+ self.survey = survey
65
67
 
66
68
  def __repr__(self) -> str:
67
69
  """Return a string representation of the Invigilator.
@@ -76,7 +78,7 @@ class InvigilatorBase(ABC):
76
78
  """Return an AgentResponseDict used in case the question-asking fails.
77
79
 
78
80
  >>> InvigilatorBase.example().get_failed_task_result()
79
- {'answer': None, 'comment': 'Failed to get response', 'question_name': 'how_feeling', ...}
81
+ {'answer': None, 'comment': 'Failed to get response', ...}
80
82
  """
81
83
  return AgentResponseDict(
82
84
  answer=None,
@@ -86,11 +88,8 @@ class InvigilatorBase(ABC):
86
88
  )
87
89
 
88
90
  def get_prompts(self) -> Dict[str, Prompt]:
89
- """Return the prompt used.
91
+ """Return the prompt used."""
90
92
 
91
- >>> InvigilatorBase.example().get_prompts()
92
- {'user_prompt': Prompt(text=\"""NA\"""), 'system_prompt': Prompt(text=\"""NA\""")}
93
- """
94
93
  return {
95
94
  "user_prompt": Prompt("NA"),
96
95
  "system_prompt": Prompt("NA"),
@@ -129,7 +128,7 @@ class InvigilatorBase(ABC):
129
128
  )
130
129
 
131
130
  @classmethod
132
- def example(cls, throw_an_exception=False):
131
+ def example(cls, throw_an_exception=False, question=None, scenario=None):
133
132
  """Return an example invigilator.
134
133
 
135
134
  >>> InvigilatorBase.example()
@@ -167,15 +166,20 @@ class InvigilatorBase(ABC):
167
166
  if throw_an_exception:
168
167
  model.throw_an_exception = True
169
168
  agent = Agent.example()
170
- question = QuestionMultipleChoice.example()
171
- scenario = Scenario.example()
169
+ # question = QuestionMultipleChoice.example()
170
+ from edsl.surveys import Survey
171
+
172
+ survey = Survey.example()
173
+ question = question or survey.questions[0]
174
+ scenario = scenario or Scenario.example()
172
175
  # memory_plan = None #memory_plan = MemoryPlan()
173
176
  from edsl import Survey
174
177
 
175
178
  memory_plan = MemoryPlan(survey=Survey.example())
176
179
  current_answers = None
180
+ from edsl.agents.PromptConstructionMixin import PromptConstructorMixin
177
181
 
178
- class InvigilatorExample(InvigilatorBase):
182
+ class InvigilatorExample(PromptConstructorMixin, InvigilatorBase):
179
183
  """An example invigilator."""
180
184
 
181
185
  async def async_answer_question(self):
@@ -188,6 +192,7 @@ class InvigilatorBase(ABC):
188
192
  agent=agent,
189
193
  question=question,
190
194
  scenario=scenario,
195
+ survey=survey,
191
196
  model=model,
192
197
  memory_plan=memory_plan,
193
198
  current_answers=current_answers,