edsl 0.1.29.dev6__py3-none-any.whl → 0.1.30__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 (65) 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 +43 -40
  5. edsl/agents/AgentList.py +23 -22
  6. edsl/agents/Invigilator.py +19 -2
  7. edsl/agents/descriptors.py +2 -1
  8. edsl/base/Base.py +289 -0
  9. edsl/config.py +2 -1
  10. edsl/conversation/car_buying.py +1 -1
  11. edsl/coop/utils.py +28 -1
  12. edsl/data/Cache.py +41 -18
  13. edsl/data/CacheEntry.py +6 -7
  14. edsl/data/SQLiteDict.py +11 -3
  15. edsl/data_transfer_models.py +4 -0
  16. edsl/jobs/Answers.py +15 -1
  17. edsl/jobs/Jobs.py +86 -33
  18. edsl/jobs/buckets/ModelBuckets.py +14 -2
  19. edsl/jobs/buckets/TokenBucket.py +32 -5
  20. edsl/jobs/interviews/Interview.py +99 -79
  21. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +18 -24
  22. edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
  23. edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
  24. edsl/jobs/tasks/TaskHistory.py +4 -3
  25. edsl/language_models/LanguageModel.py +17 -17
  26. edsl/language_models/ModelList.py +1 -1
  27. edsl/language_models/repair.py +8 -7
  28. edsl/notebooks/Notebook.py +16 -10
  29. edsl/questions/QuestionBase.py +6 -2
  30. edsl/questions/QuestionBudget.py +5 -6
  31. edsl/questions/QuestionCheckBox.py +7 -3
  32. edsl/questions/QuestionExtract.py +5 -3
  33. edsl/questions/QuestionFreeText.py +7 -5
  34. edsl/questions/QuestionFunctional.py +34 -5
  35. edsl/questions/QuestionList.py +3 -4
  36. edsl/questions/QuestionMultipleChoice.py +68 -12
  37. edsl/questions/QuestionNumerical.py +4 -3
  38. edsl/questions/QuestionRank.py +5 -3
  39. edsl/questions/__init__.py +4 -3
  40. edsl/questions/descriptors.py +46 -4
  41. edsl/results/DatasetExportMixin.py +570 -0
  42. edsl/results/Result.py +66 -70
  43. edsl/results/Results.py +160 -68
  44. edsl/results/ResultsDBMixin.py +7 -3
  45. edsl/results/ResultsExportMixin.py +22 -537
  46. edsl/results/ResultsGGMixin.py +3 -3
  47. edsl/results/ResultsToolsMixin.py +1 -4
  48. edsl/scenarios/FileStore.py +299 -0
  49. edsl/scenarios/Scenario.py +16 -24
  50. edsl/scenarios/ScenarioList.py +25 -14
  51. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  52. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  53. edsl/scenarios/__init__.py +1 -0
  54. edsl/study/Study.py +5 -7
  55. edsl/surveys/MemoryPlan.py +11 -4
  56. edsl/surveys/Survey.py +52 -15
  57. edsl/surveys/SurveyExportMixin.py +4 -2
  58. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  59. edsl/utilities/__init__.py +21 -21
  60. edsl/utilities/interface.py +66 -45
  61. edsl/utilities/utilities.py +11 -13
  62. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/METADATA +1 -1
  63. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/RECORD +65 -61
  64. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
  65. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dist-info}/LICENSE +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
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
41
  from edsl.conjure.Conjure import Conjure
41
- from edsl.language_models.ModelList import ModelList
42
42
  from edsl.coop.coop import Coop
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.29.dev6"
1
+ __version__ = "0.1.30"
edsl/agents/Agent.py CHANGED
@@ -4,28 +4,16 @@ from __future__ import annotations
4
4
  import copy
5
5
  import inspect
6
6
  import types
7
- from typing import Any, Callable, Optional, Union, Dict, Sequence
8
-
9
- from rich.table import Table
10
-
7
+ from typing import Callable, Optional, Union
8
+ from uuid import uuid4
11
9
  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
10
+
15
11
  from edsl.exceptions.agents import (
16
12
  AgentCombinationError,
17
13
  AgentDirectAnswerFunctionError,
18
14
  AgentDynamicTraitsFunctionError,
19
15
  )
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
16
+
29
17
  from edsl.agents.descriptors import (
30
18
  TraitsDescriptor,
31
19
  CodebookDescriptor,
@@ -38,10 +26,6 @@ from edsl.utilities.decorators import (
38
26
  remove_edsl_version,
39
27
  )
40
28
  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
29
  from edsl.utilities.restricted_python import create_restricted_function
46
30
 
47
31
 
@@ -156,6 +140,8 @@ class Agent(Base):
156
140
  self.current_question = None
157
141
 
158
142
  if traits_presentation_template is not None:
143
+ from edsl.prompts.library.agent_persona import AgentPersona
144
+
159
145
  self.traits_presentation_template = traits_presentation_template
160
146
  self.agent_persona = AgentPersona(text=self.traits_presentation_template)
161
147
 
@@ -276,8 +262,8 @@ class Agent(Base):
276
262
  def create_invigilator(
277
263
  self,
278
264
  *,
279
- question: QuestionBase,
280
- cache,
265
+ question: "QuestionBase",
266
+ cache: "Cache",
281
267
  survey: Optional["Survey"] = None,
282
268
  scenario: Optional[Scenario] = None,
283
269
  model: Optional[LanguageModel] = None,
@@ -286,7 +272,7 @@ class Agent(Base):
286
272
  current_answers: Optional[dict] = None,
287
273
  iteration: int = 1,
288
274
  sidecar_model=None,
289
- ) -> InvigilatorBase:
275
+ ) -> "InvigilatorBase":
290
276
  """Create an Invigilator.
291
277
 
292
278
  An invigilator is an object that is responsible for administering a question to an agent.
@@ -300,6 +286,8 @@ class Agent(Base):
300
286
  An invigator is an object that is responsible for administering a question to an agent and
301
287
  recording the responses.
302
288
  """
289
+ from edsl import Model, Scenario
290
+
303
291
  cache = cache
304
292
  self.current_question = question
305
293
  model = model or Model()
@@ -321,13 +309,13 @@ class Agent(Base):
321
309
  async def async_answer_question(
322
310
  self,
323
311
  *,
324
- question: QuestionBase,
325
- cache: Cache,
326
- scenario: Optional[Scenario] = None,
312
+ question: "QuestionBase",
313
+ cache: "Cache",
314
+ scenario: Optional["Scenario"] = None,
327
315
  survey: Optional["Survey"] = None,
328
- model: Optional[LanguageModel] = None,
316
+ model: Optional["LanguageModel"] = None,
329
317
  debug: bool = False,
330
- memory_plan: Optional[MemoryPlan] = None,
318
+ memory_plan: Optional["MemoryPlan"] = None,
331
319
  current_answers: Optional[dict] = None,
332
320
  iteration: int = 0,
333
321
  ) -> AgentResponseDict:
@@ -371,22 +359,35 @@ class Agent(Base):
371
359
 
372
360
  def _create_invigilator(
373
361
  self,
374
- question: QuestionBase,
375
- cache: Optional[Cache] = None,
376
- scenario: Optional[Scenario] = None,
377
- model: Optional[LanguageModel] = None,
362
+ question: "QuestionBase",
363
+ cache: Optional["Cache"] = None,
364
+ scenario: Optional["Scenario"] = None,
365
+ model: Optional["LanguageModel"] = None,
378
366
  survey: Optional["Survey"] = None,
379
367
  debug: bool = False,
380
- memory_plan: Optional[MemoryPlan] = None,
368
+ memory_plan: Optional["MemoryPlan"] = None,
381
369
  current_answers: Optional[dict] = None,
382
370
  iteration: int = 0,
383
371
  sidecar_model=None,
384
- ) -> InvigilatorBase:
372
+ ) -> "InvigilatorBase":
385
373
  """Create an Invigilator."""
374
+ from edsl import Model
375
+ from edsl import Scenario
376
+
386
377
  model = model or Model()
387
378
  scenario = scenario or Scenario()
388
379
 
380
+ from edsl.agents.Invigilator import (
381
+ InvigilatorDebug,
382
+ InvigilatorHuman,
383
+ InvigilatorFunctional,
384
+ InvigilatorAI,
385
+ InvigilatorBase,
386
+ )
387
+
389
388
  if cache is None:
389
+ from edsl.data.Cache import Cache
390
+
390
391
  cache = Cache()
391
392
 
392
393
  if debug:
@@ -502,7 +503,6 @@ class Agent(Base):
502
503
  f"'{type(self).__name__}' object has no attribute '{name}'"
503
504
  )
504
505
 
505
-
506
506
  def __getstate__(self):
507
507
  state = self.__dict__.copy()
508
508
  # Include any additional state that needs to be serialized
@@ -675,6 +675,8 @@ class Agent(Base):
675
675
  >>> a.rich_print()
676
676
  <rich.table.Table object at ...>
677
677
  """
678
+ from rich.table import Table
679
+
678
680
  table_data, column_names = self._table()
679
681
  table = Table(title=f"{self.__class__.__name__} Attributes")
680
682
  for column in column_names:
@@ -687,13 +689,14 @@ class Agent(Base):
687
689
  return table
688
690
 
689
691
  @classmethod
690
- def example(cls) -> Agent:
691
- """Return an example agent.
692
+ def example(cls, randomize: bool = False) -> Agent:
693
+ """
694
+ Returns an example Agent instance.
692
695
 
693
- >>> Agent.example()
694
- Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})
696
+ :param randomize: If True, adds a random string to the value of an example key.
695
697
  """
696
- return cls(traits={"age": 22, "hair": "brown", "height": 5.5})
698
+ addition = "" if not randomize else str(uuid4())
699
+ return cls(traits={"age": 22, "hair": f"brown{addition}", "height": 5.5})
697
700
 
698
701
  def code(self) -> str:
699
702
  """Return the code for the agent.
edsl/agents/AgentList.py CHANGED
@@ -11,28 +11,21 @@ Example usage:
11
11
  """
12
12
 
13
13
  from __future__ import annotations
14
+ import csv
15
+ import json
14
16
  from collections import UserList
15
- from typing import Optional, Union, Sequence, List, Any
17
+ from typing import Any, List, Optional, Union
16
18
  from rich import print_json
17
19
  from rich.table import Table
18
- import json
19
- import csv
20
-
21
-
22
20
  from simpleeval import EvalWithCompoundTypes
23
-
24
21
  from edsl.Base import Base
25
- from edsl.agents import Agent
26
- from edsl.utilities.decorators import (
27
- add_edsl_version,
28
- remove_edsl_version,
29
- )
22
+ from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
30
23
 
31
24
 
32
25
  class AgentList(UserList, Base):
33
26
  """A list of Agents."""
34
27
 
35
- def __init__(self, data: Optional[list[Agent]] = None):
28
+ def __init__(self, data: Optional[list["Agent"]] = None):
36
29
  """Initialize a new AgentList.
37
30
 
38
31
  :param data: A list of Agents.
@@ -77,6 +70,7 @@ class AgentList(UserList, Base):
77
70
  def select(self, *traits) -> AgentList:
78
71
  """Selects agents with only the references traits.
79
72
 
73
+ >>> from edsl.agents.Agent import Agent
80
74
  >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
81
75
  >>> al.select('a')
82
76
  AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
@@ -94,12 +88,13 @@ class AgentList(UserList, Base):
94
88
  """
95
89
  Filter a list of agents based on an expression.
96
90
 
91
+ >>> from edsl.agents.Agent import Agent
97
92
  >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
98
93
  >>> al.filter("b == 2")
99
94
  AgentList([Agent(traits = {'a': 1, 'b': 2})])
100
95
  """
101
96
 
102
- def create_evaluator(agent: Agent):
97
+ def create_evaluator(agent: "Agent"):
103
98
  """Create an evaluator for the given result.
104
99
  The 'combined_dict' is a mapping of all values for that Result object.
105
100
  """
@@ -133,6 +128,8 @@ class AgentList(UserList, Base):
133
128
 
134
129
  :param file_path: The path to the CSV file.
135
130
  """
131
+ from edsl.agents.Agent import Agent
132
+
136
133
  agent_list = []
137
134
  with open(file_path, "r") as f:
138
135
  reader = csv.DictReader(f)
@@ -153,7 +150,7 @@ class AgentList(UserList, Base):
153
150
  """Remove traits from the AgentList.
154
151
 
155
152
  :param traits: The traits to remove.
156
-
153
+ >>> from edsl.agents.Agent import Agent
157
154
  >>> al = AgentList([Agent({'age': 22, 'hair': 'brown', 'height': 5.5}), Agent({'age': 22, 'hair': 'brown', 'height': 5.5})])
158
155
  >>> al.remove_trait('age')
159
156
  AgentList([Agent(traits = {'hair': 'brown', 'height': 5.5}), Agent(traits = {'hair': 'brown', 'height': 5.5})])
@@ -222,25 +219,27 @@ class AgentList(UserList, Base):
222
219
  """Deserialize the dictionary back to an AgentList object.
223
220
 
224
221
  :param: data: A dictionary representing an AgentList.
225
-
222
+ >>> from edsl.agents.Agent import Agent
226
223
  >>> al = AgentList([Agent.example(), Agent.example()])
227
224
  >>> al2 = AgentList.from_dict(al.to_dict())
228
225
  >>> al2 == al
229
226
  True
230
227
  """
228
+ from edsl.agents.Agent import Agent
229
+
231
230
  agents = [Agent.from_dict(agent_dict) for agent_dict in data["agent_list"]]
232
231
  return cls(agents)
233
232
 
234
233
  @classmethod
235
- def example(cls) -> "AgentList":
236
- """Return an example AgentList.
237
-
238
- >>> al = AgentList.example()
239
- >>> len(al)
240
- 2
234
+ def example(cls, randomize: bool = False) -> AgentList:
235
+ """
236
+ Returns an example AgentList instance.
241
237
 
238
+ :param randomize: If True, uses Agent's randomize method.
242
239
  """
243
- return cls([Agent.example(), Agent.example()])
240
+ from edsl.agents.Agent import Agent
241
+
242
+ return cls([Agent.example(randomize), Agent.example(randomize)])
244
243
 
245
244
  @classmethod
246
245
  def from_list(self, trait_name: str, values: List[Any]):
@@ -249,6 +248,8 @@ class AgentList(UserList, Base):
249
248
  :param trait_name: The name of the trait.
250
249
  :param values: A list of values.
251
250
  """
251
+ from edsl.agents.Agent import Agent
252
+
252
253
  return AgentList([Agent({trait_name: value}) for value in values])
253
254
 
254
255
  def __mul__(self, other: AgentList) -> AgentList:
@@ -74,15 +74,30 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
74
74
 
75
75
  This cleans up the raw response to make it suitable to pass to AgentResponseDict.
76
76
  """
77
- # not actually used, but this removes the temptation to delete agent from the signature
78
77
  _ = agent
79
78
  try:
80
79
  response = question._validate_answer(raw_response)
81
80
  except Exception as e:
81
+ """If the response is invalid, remove it from the cache and raise the exception."""
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
+ question_dict = self.survey.question_names_to_questions()
86
+ for other_question, answer in self.current_answers.items():
87
+ if other_question in question_dict:
88
+ question_dict[other_question].answer = answer
89
+ else:
90
+ # adds a comment to the question
91
+ if (
92
+ new_question := other_question.split("_comment")[0]
93
+ ) in question_dict:
94
+ question_dict[new_question].comment = answer
95
+
96
+ combined_dict = {**question_dict, **scenario}
97
+ answer = question._translate_answer_code_to_answer(
98
+ response["answer"], combined_dict
99
+ )
100
+ # breakpoint()
86
101
  data = {
87
102
  "answer": answer,
88
103
  "comment": response.get(
@@ -93,6 +108,8 @@ class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
93
108
  "cached_response": raw_response.get("cached_response", None),
94
109
  "usage": raw_response.get("usage", {}),
95
110
  "raw_model_response": raw_model_response,
111
+ "cache_used": raw_response.get("cache_used", False),
112
+ "cache_key": raw_response.get("cache_key", None),
96
113
  }
97
114
  return AgentResponseDict(**data)
98
115
 
@@ -1,7 +1,6 @@
1
1
  """This module contains the descriptors used to set the attributes of the Agent class."""
2
2
 
3
3
  from typing import Dict
4
- from edsl.utilities.utilities import is_valid_variable_name
5
4
  from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
6
5
 
7
6
 
@@ -30,6 +29,8 @@ class TraitsDescriptor:
30
29
 
31
30
  def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
32
31
  """Set the value of the attribute."""
32
+ from edsl.utilities.utilities import is_valid_variable_name
33
+
33
34
  for key, value in traits_dict.items():
34
35
  if key == "name":
35
36
  raise AgentNameError(