edsl 0.1.32__py3-none-any.whl → 0.1.33__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 (181) hide show
  1. edsl/Base.py +9 -3
  2. edsl/TemplateLoader.py +24 -0
  3. edsl/__init__.py +8 -3
  4. edsl/__version__.py +1 -1
  5. edsl/agents/Agent.py +40 -8
  6. edsl/agents/AgentList.py +43 -0
  7. edsl/agents/Invigilator.py +135 -219
  8. edsl/agents/InvigilatorBase.py +148 -59
  9. edsl/agents/{PromptConstructionMixin.py → PromptConstructor.py} +138 -89
  10. edsl/agents/__init__.py +1 -0
  11. edsl/auto/AutoStudy.py +117 -0
  12. edsl/auto/StageBase.py +230 -0
  13. edsl/auto/StageGenerateSurvey.py +178 -0
  14. edsl/auto/StageLabelQuestions.py +125 -0
  15. edsl/auto/StagePersona.py +61 -0
  16. edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
  17. edsl/auto/StagePersonaDimensionValues.py +74 -0
  18. edsl/auto/StagePersonaDimensions.py +69 -0
  19. edsl/auto/StageQuestions.py +73 -0
  20. edsl/auto/SurveyCreatorPipeline.py +21 -0
  21. edsl/auto/utilities.py +224 -0
  22. edsl/config.py +47 -56
  23. edsl/coop/PriceFetcher.py +58 -0
  24. edsl/coop/coop.py +50 -7
  25. edsl/data/Cache.py +35 -1
  26. edsl/data_transfer_models.py +73 -38
  27. edsl/enums.py +4 -0
  28. edsl/exceptions/language_models.py +25 -1
  29. edsl/exceptions/questions.py +62 -5
  30. edsl/exceptions/results.py +4 -0
  31. edsl/inference_services/AnthropicService.py +13 -11
  32. edsl/inference_services/AwsBedrock.py +19 -17
  33. edsl/inference_services/AzureAI.py +37 -20
  34. edsl/inference_services/GoogleService.py +16 -12
  35. edsl/inference_services/GroqService.py +2 -0
  36. edsl/inference_services/InferenceServiceABC.py +58 -3
  37. edsl/inference_services/MistralAIService.py +120 -0
  38. edsl/inference_services/OpenAIService.py +48 -54
  39. edsl/inference_services/TestService.py +80 -0
  40. edsl/inference_services/TogetherAIService.py +170 -0
  41. edsl/inference_services/models_available_cache.py +0 -6
  42. edsl/inference_services/registry.py +6 -0
  43. edsl/jobs/Answers.py +10 -12
  44. edsl/jobs/FailedQuestion.py +78 -0
  45. edsl/jobs/Jobs.py +37 -22
  46. edsl/jobs/buckets/BucketCollection.py +24 -15
  47. edsl/jobs/buckets/TokenBucket.py +93 -14
  48. edsl/jobs/interviews/Interview.py +366 -78
  49. edsl/jobs/interviews/{interview_exception_tracking.py → InterviewExceptionCollection.py} +14 -68
  50. edsl/jobs/interviews/InterviewExceptionEntry.py +85 -19
  51. edsl/jobs/runners/JobsRunnerAsyncio.py +146 -175
  52. edsl/jobs/runners/JobsRunnerStatus.py +331 -0
  53. edsl/jobs/tasks/QuestionTaskCreator.py +30 -23
  54. edsl/jobs/tasks/TaskHistory.py +148 -213
  55. edsl/language_models/LanguageModel.py +261 -156
  56. edsl/language_models/ModelList.py +2 -2
  57. edsl/language_models/RegisterLanguageModelsMeta.py +14 -29
  58. edsl/language_models/fake_openai_call.py +15 -0
  59. edsl/language_models/fake_openai_service.py +61 -0
  60. edsl/language_models/registry.py +23 -6
  61. edsl/language_models/repair.py +0 -19
  62. edsl/language_models/utilities.py +61 -0
  63. edsl/notebooks/Notebook.py +20 -2
  64. edsl/prompts/Prompt.py +52 -2
  65. edsl/questions/AnswerValidatorMixin.py +23 -26
  66. edsl/questions/QuestionBase.py +330 -249
  67. edsl/questions/QuestionBaseGenMixin.py +133 -0
  68. edsl/questions/QuestionBasePromptsMixin.py +266 -0
  69. edsl/questions/QuestionBudget.py +99 -41
  70. edsl/questions/QuestionCheckBox.py +227 -35
  71. edsl/questions/QuestionExtract.py +98 -27
  72. edsl/questions/QuestionFreeText.py +52 -29
  73. edsl/questions/QuestionFunctional.py +7 -0
  74. edsl/questions/QuestionList.py +141 -22
  75. edsl/questions/QuestionMultipleChoice.py +159 -65
  76. edsl/questions/QuestionNumerical.py +88 -46
  77. edsl/questions/QuestionRank.py +182 -24
  78. edsl/questions/Quick.py +41 -0
  79. edsl/questions/RegisterQuestionsMeta.py +31 -12
  80. edsl/questions/ResponseValidatorABC.py +170 -0
  81. edsl/questions/__init__.py +3 -4
  82. edsl/questions/decorators.py +21 -0
  83. edsl/questions/derived/QuestionLikertFive.py +10 -5
  84. edsl/questions/derived/QuestionLinearScale.py +15 -2
  85. edsl/questions/derived/QuestionTopK.py +10 -1
  86. edsl/questions/derived/QuestionYesNo.py +24 -3
  87. edsl/questions/descriptors.py +43 -7
  88. edsl/questions/prompt_templates/question_budget.jinja +13 -0
  89. edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
  90. edsl/questions/prompt_templates/question_extract.jinja +11 -0
  91. edsl/questions/prompt_templates/question_free_text.jinja +3 -0
  92. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
  93. edsl/questions/prompt_templates/question_list.jinja +17 -0
  94. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
  95. edsl/questions/prompt_templates/question_numerical.jinja +37 -0
  96. edsl/questions/question_registry.py +6 -2
  97. edsl/questions/templates/__init__.py +0 -0
  98. edsl/questions/templates/budget/__init__.py +0 -0
  99. edsl/questions/templates/budget/answering_instructions.jinja +7 -0
  100. edsl/questions/templates/budget/question_presentation.jinja +7 -0
  101. edsl/questions/templates/checkbox/__init__.py +0 -0
  102. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
  103. edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
  104. edsl/questions/templates/extract/__init__.py +0 -0
  105. edsl/questions/templates/extract/answering_instructions.jinja +7 -0
  106. edsl/questions/templates/extract/question_presentation.jinja +1 -0
  107. edsl/questions/templates/free_text/__init__.py +0 -0
  108. edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  109. edsl/questions/templates/free_text/question_presentation.jinja +1 -0
  110. edsl/questions/templates/likert_five/__init__.py +0 -0
  111. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
  112. edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
  113. edsl/questions/templates/linear_scale/__init__.py +0 -0
  114. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
  115. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
  116. edsl/questions/templates/list/__init__.py +0 -0
  117. edsl/questions/templates/list/answering_instructions.jinja +4 -0
  118. edsl/questions/templates/list/question_presentation.jinja +5 -0
  119. edsl/questions/templates/multiple_choice/__init__.py +0 -0
  120. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
  121. edsl/questions/templates/multiple_choice/html.jinja +0 -0
  122. edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
  123. edsl/questions/templates/numerical/__init__.py +0 -0
  124. edsl/questions/templates/numerical/answering_instructions.jinja +8 -0
  125. edsl/questions/templates/numerical/question_presentation.jinja +7 -0
  126. edsl/questions/templates/rank/__init__.py +0 -0
  127. edsl/questions/templates/rank/answering_instructions.jinja +11 -0
  128. edsl/questions/templates/rank/question_presentation.jinja +15 -0
  129. edsl/questions/templates/top_k/__init__.py +0 -0
  130. edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
  131. edsl/questions/templates/top_k/question_presentation.jinja +22 -0
  132. edsl/questions/templates/yes_no/__init__.py +0 -0
  133. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
  134. edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
  135. edsl/results/Dataset.py +20 -0
  136. edsl/results/DatasetExportMixin.py +46 -48
  137. edsl/results/DatasetTree.py +145 -0
  138. edsl/results/Result.py +32 -5
  139. edsl/results/Results.py +135 -46
  140. edsl/results/ResultsDBMixin.py +3 -3
  141. edsl/results/Selector.py +118 -0
  142. edsl/results/tree_explore.py +115 -0
  143. edsl/scenarios/FileStore.py +71 -10
  144. edsl/scenarios/Scenario.py +96 -25
  145. edsl/scenarios/ScenarioImageMixin.py +2 -2
  146. edsl/scenarios/ScenarioList.py +361 -39
  147. edsl/scenarios/ScenarioListExportMixin.py +9 -0
  148. edsl/scenarios/ScenarioListPdfMixin.py +150 -4
  149. edsl/study/SnapShot.py +8 -1
  150. edsl/study/Study.py +32 -0
  151. edsl/surveys/Rule.py +10 -1
  152. edsl/surveys/RuleCollection.py +21 -5
  153. edsl/surveys/Survey.py +637 -311
  154. edsl/surveys/SurveyExportMixin.py +71 -9
  155. edsl/surveys/SurveyFlowVisualizationMixin.py +2 -1
  156. edsl/surveys/SurveyQualtricsImport.py +75 -4
  157. edsl/surveys/instructions/ChangeInstruction.py +47 -0
  158. edsl/surveys/instructions/Instruction.py +34 -0
  159. edsl/surveys/instructions/InstructionCollection.py +77 -0
  160. edsl/surveys/instructions/__init__.py +0 -0
  161. edsl/templates/error_reporting/base.html +24 -0
  162. edsl/templates/error_reporting/exceptions_by_model.html +35 -0
  163. edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
  164. edsl/templates/error_reporting/exceptions_by_type.html +17 -0
  165. edsl/templates/error_reporting/interview_details.html +116 -0
  166. edsl/templates/error_reporting/interviews.html +10 -0
  167. edsl/templates/error_reporting/overview.html +5 -0
  168. edsl/templates/error_reporting/performance_plot.html +2 -0
  169. edsl/templates/error_reporting/report.css +74 -0
  170. edsl/templates/error_reporting/report.html +118 -0
  171. edsl/templates/error_reporting/report.js +25 -0
  172. edsl/utilities/utilities.py +9 -1
  173. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/METADATA +5 -2
  174. edsl-0.1.33.dist-info/RECORD +295 -0
  175. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +0 -286
  176. edsl/jobs/interviews/retry_management.py +0 -37
  177. edsl/jobs/runners/JobsRunnerStatusMixin.py +0 -333
  178. edsl/utilities/gcp_bucket/simple_example.py +0 -9
  179. edsl-0.1.32.dist-info/RECORD +0 -209
  180. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/LICENSE +0 -0
  181. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/WHEEL +0 -0
edsl/Base.py CHANGED
@@ -47,21 +47,27 @@ class PersistenceMixin:
47
47
  self,
48
48
  description: Optional[str] = None,
49
49
  visibility: Optional[str] = "unlisted",
50
+ expected_parrot_url: Optional[str] = None,
50
51
  ):
51
52
  """Post the object to coop."""
52
53
  from edsl.coop import Coop
53
54
 
54
- c = Coop()
55
+ c = Coop(url=expected_parrot_url)
55
56
  return c.create(self, description, visibility)
56
57
 
57
58
  @classmethod
58
- def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
59
+ def pull(
60
+ cls,
61
+ uuid: Optional[Union[str, UUID]] = None,
62
+ url: Optional[str] = None,
63
+ expected_parrot_url: Optional[str] = None,
64
+ ):
59
65
  """Pull the object from coop."""
60
66
  from edsl.coop import Coop
61
67
  from edsl.coop.utils import ObjectRegistry
62
68
 
63
69
  object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
64
- coop = Coop()
70
+ coop = Coop(url=expected_parrot_url)
65
71
  return coop.get(uuid, url, object_type)
66
72
 
67
73
  @classmethod
edsl/TemplateLoader.py ADDED
@@ -0,0 +1,24 @@
1
+ from importlib import resources
2
+ from jinja2 import BaseLoader, TemplateNotFound
3
+ import os
4
+
5
+
6
+ class TemplateLoader(BaseLoader):
7
+ def __init__(self, package_name, templates_dir):
8
+ self.package_name = package_name
9
+ self.templates_dir = templates_dir
10
+
11
+ def get_source(self, environment, template):
12
+ try:
13
+ parts = [self.templates_dir] + template.split("/")
14
+ template_path = os.path.join(*parts)
15
+
16
+ # Use resources.files() to get a Traversable object
17
+ templates = resources.files(self.package_name).joinpath(self.templates_dir)
18
+
19
+ # Use the read_text() method of the Traversable object
20
+ content = templates.joinpath(template).read_text()
21
+
22
+ return content, None, lambda: True
23
+ except FileNotFoundError:
24
+ raise TemplateNotFound(template)
edsl/__init__.py CHANGED
@@ -8,9 +8,10 @@ from edsl.__version__ import __version__
8
8
  from edsl.config import Config, CONFIG
9
9
  from edsl.agents.Agent import Agent
10
10
  from edsl.agents.AgentList import AgentList
11
+
11
12
  from edsl.questions import QuestionBase
13
+ from edsl.questions.question_registry import Question
12
14
  from edsl.questions import QuestionMultipleChoice
13
- from edsl.questions import QuestionBudget
14
15
  from edsl.questions import QuestionCheckBox
15
16
  from edsl.questions import QuestionExtract
16
17
  from edsl.questions import QuestionFreeText
@@ -19,10 +20,11 @@ from edsl.questions import QuestionLikertFive
19
20
  from edsl.questions import QuestionList
20
21
  from edsl.questions import QuestionLinearScale
21
22
  from edsl.questions import QuestionNumerical
23
+ from edsl.questions import QuestionYesNo
24
+ from edsl.questions import QuestionBudget
22
25
  from edsl.questions import QuestionRank
23
26
  from edsl.questions import QuestionTopK
24
- from edsl.questions import QuestionYesNo
25
- from edsl.questions.question_registry import Question
27
+
26
28
  from edsl.scenarios import Scenario
27
29
  from edsl.scenarios import ScenarioList
28
30
 
@@ -40,3 +42,6 @@ from edsl.notebooks.Notebook import Notebook
40
42
  from edsl.study.Study import Study
41
43
  from edsl.conjure.Conjure import Conjure
42
44
  from edsl.coop.coop import Coop
45
+
46
+ from edsl.surveys.instructions.Instruction import Instruction
47
+ from edsl.surveys.instructions.ChangeInstruction import ChangeInstruction
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.32"
1
+ __version__ = "0.1.33"
edsl/agents/Agent.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  import copy
5
5
  import inspect
6
6
  import types
7
- from typing import Callable, Optional, Union
7
+ from typing import Callable, Optional, Union, Any
8
8
  from uuid import uuid4
9
9
  from edsl.Base import Base
10
10
 
@@ -228,7 +228,12 @@ class Agent(Base):
228
228
  if hasattr(self, "answer_question_directly"):
229
229
  delattr(self, "answer_question_directly")
230
230
 
231
- def add_direct_question_answering_method(self, method: Callable) -> None:
231
+ def add_direct_question_answering_method(
232
+ self,
233
+ method: Callable,
234
+ validate_response: bool = False,
235
+ translate_response: bool = False,
236
+ ) -> None:
232
237
  """Add a method to the agent that can answer a particular question type.
233
238
 
234
239
  :param method: A method that can answer a question directly.
@@ -249,6 +254,9 @@ class Agent(Base):
249
254
  )
250
255
  # print("Warning: overwriting existing answer_question_directly method")
251
256
 
257
+ self.validate_response = validate_response
258
+ self.translate_response = translate_response
259
+
252
260
  signature = inspect.signature(method)
253
261
  for argument in ["question", "scenario", "self"]:
254
262
  if argument not in signature.parameters:
@@ -272,6 +280,7 @@ class Agent(Base):
272
280
  current_answers: Optional[dict] = None,
273
281
  iteration: int = 1,
274
282
  sidecar_model=None,
283
+ raise_validation_errors: bool = True,
275
284
  ) -> "InvigilatorBase":
276
285
  """Create an Invigilator.
277
286
 
@@ -303,7 +312,12 @@ class Agent(Base):
303
312
  iteration=iteration,
304
313
  cache=cache,
305
314
  sidecar_model=sidecar_model,
315
+ raise_validation_errors=raise_validation_errors,
306
316
  )
317
+ if hasattr(self, "validate_response"):
318
+ invigilator.validate_response = self.validate_response
319
+ if hasattr(self, "translate_response"):
320
+ invigilator.translate_response = self.translate_response
307
321
  return invigilator
308
322
 
309
323
  async def async_answer_question(
@@ -334,8 +348,8 @@ class Agent(Base):
334
348
  >>> a.add_direct_question_answering_method(lambda self, question, scenario: "I am a direct answer.")
335
349
  >>> from edsl import QuestionFreeText
336
350
  >>> q = QuestionFreeText.example()
337
- >>> a.answer_question(question = q, cache = False)
338
- {'answer': 'I am a direct answer.', 'comment': 'This is a real survey response from a human.', ...}
351
+ >>> a.answer_question(question = q, cache = False).answer
352
+ 'I am a direct answer.'
339
353
 
340
354
  This is a function where an agent returns an answer to a particular question.
341
355
  However, there are several different ways an agent can answer a question, so the
@@ -369,6 +383,7 @@ class Agent(Base):
369
383
  current_answers: Optional[dict] = None,
370
384
  iteration: int = 0,
371
385
  sidecar_model=None,
386
+ raise_validation_errors: bool = True,
372
387
  ) -> "InvigilatorBase":
373
388
  """Create an Invigilator."""
374
389
  from edsl import Model
@@ -378,7 +393,6 @@ class Agent(Base):
378
393
  scenario = scenario or Scenario()
379
394
 
380
395
  from edsl.agents.Invigilator import (
381
- InvigilatorDebug,
382
396
  InvigilatorHuman,
383
397
  InvigilatorFunctional,
384
398
  InvigilatorAI,
@@ -391,8 +405,9 @@ class Agent(Base):
391
405
  cache = Cache()
392
406
 
393
407
  if debug:
408
+ raise NotImplementedError("Debug mode is not yet implemented.")
394
409
  # use the question's _simulate_answer method
395
- invigilator_class = InvigilatorDebug
410
+ # invigilator_class = InvigilatorDebug
396
411
  elif hasattr(question, "answer_question_directly"):
397
412
  # It's a functional question and the answer only depends on the agent's traits & the scenario
398
413
  invigilator_class = InvigilatorFunctional
@@ -422,6 +437,7 @@ class Agent(Base):
422
437
  iteration=iteration,
423
438
  cache=cache,
424
439
  sidecar_model=sidecar_model,
440
+ raise_validation_errors=raise_validation_errors,
425
441
  )
426
442
  return invigilator
427
443
 
@@ -497,8 +513,8 @@ class Agent(Base):
497
513
  if name == "has_dynamic_traits_function":
498
514
  return self.has_dynamic_traits_function
499
515
 
500
- if name in self.traits:
501
- return self.traits[name]
516
+ if name in self._traits:
517
+ return self._traits[name]
502
518
  raise AttributeError(
503
519
  f"'{type(self).__name__}' object has no attribute '{name}'"
504
520
  )
@@ -640,6 +656,22 @@ class Agent(Base):
640
656
  column_names = ["Attribute", "Value"]
641
657
  return table_data, column_names
642
658
 
659
+ def add_trait(self, trait_name_or_dict: str, value: Optional[Any] = None) -> Agent:
660
+ """Adds a trait to an agent and returns that agent"""
661
+ if isinstance(trait_name_or_dict, dict) and value is None:
662
+ self.traits.update(trait_name_or_dict)
663
+ return self
664
+
665
+ if isinstance(trait_name_or_dict, dict) and value:
666
+ raise ValueError(f"You passed a dict: {trait_name_or_dict}")
667
+
668
+ if isinstance(trait_name_or_dict, str):
669
+ trait = trait_name_or_dict
670
+ self.traits[trait] = value
671
+ return self
672
+
673
+ raise Exception("Something is not right with adding")
674
+
643
675
  def remove_trait(self, trait: str) -> Agent:
644
676
  """Remove a trait from the agent.
645
677
 
edsl/agents/AgentList.py CHANGED
@@ -21,6 +21,12 @@ from simpleeval import EvalWithCompoundTypes
21
21
  from edsl.Base import Base
22
22
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
23
23
 
24
+ from collections.abc import Iterable
25
+
26
+
27
+ def is_iterable(obj):
28
+ return isinstance(obj, Iterable)
29
+
24
30
 
25
31
  class AgentList(UserList, Base):
26
32
  """A list of Agents."""
@@ -111,6 +117,13 @@ class AgentList(UserList, Base):
111
117
 
112
118
  return AgentList(new_data)
113
119
 
120
+ @property
121
+ def all_traits(self):
122
+ d = {}
123
+ for agent in self:
124
+ d.update(agent.traits)
125
+ return list(d.keys())
126
+
114
127
  @classmethod
115
128
  def from_csv(cls, file_path: str):
116
129
  """Load AgentList from a CSV file.
@@ -159,6 +172,36 @@ class AgentList(UserList, Base):
159
172
  _ = agent.remove_trait(trait)
160
173
  return self
161
174
 
175
+ def add_trait(self, trait, values):
176
+ """Adds a new trait to every agent, with values taken from values.
177
+
178
+ :param trait: The name of the trait.
179
+ :param values: The valeues(s) of the trait. If a single value is passed, it is used for all agents.
180
+
181
+ >>> al = AgentList.example()
182
+ >>> al.add_trait('new_trait', 1)
183
+ AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5, 'new_trait': 1})])
184
+ >>> al.select('new_trait').to_scenario_list().to_list()
185
+ [1, 1]
186
+ >>> al.add_trait('new_trait', [1, 2, 3])
187
+ Traceback (most recent call last):
188
+ ...
189
+ ValueError: The passed values have to be the same length as the agent list.
190
+ """
191
+ if not is_iterable(values):
192
+ value = values
193
+ for agent in self.data:
194
+ agent.add_trait(trait, value)
195
+ return self
196
+
197
+ if len(values) != len(self):
198
+ raise ValueError(
199
+ "The passed values have to be the same length as the agent list."
200
+ )
201
+ for agent, value in zip(self.data, values):
202
+ agent.add_trait(trait, value)
203
+ return self
204
+
162
205
  @staticmethod
163
206
  def get_codebook(file_path: str):
164
207
  """Return the codebook for a CSV file.