edsl 0.1.29__py3-none-any.whl → 0.1.29.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.
Files changed (72) hide show
  1. edsl/Base.py +18 -18
  2. edsl/__init__.py +23 -23
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +41 -77
  5. edsl/agents/AgentList.py +9 -19
  6. edsl/agents/Invigilator.py +1 -19
  7. edsl/agents/InvigilatorBase.py +10 -15
  8. edsl/agents/PromptConstructionMixin.py +100 -342
  9. edsl/agents/descriptors.py +1 -2
  10. edsl/config.py +1 -2
  11. edsl/conjure/InputData.py +8 -39
  12. edsl/coop/coop.py +150 -187
  13. edsl/coop/utils.py +75 -43
  14. edsl/data/Cache.py +5 -19
  15. edsl/data/SQLiteDict.py +3 -11
  16. edsl/jobs/Answers.py +1 -15
  17. edsl/jobs/Jobs.py +46 -90
  18. edsl/jobs/buckets/ModelBuckets.py +2 -4
  19. edsl/jobs/buckets/TokenBucket.py +2 -1
  20. edsl/jobs/interviews/Interview.py +9 -3
  21. edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
  22. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +10 -15
  23. edsl/jobs/runners/JobsRunnerAsyncio.py +25 -21
  24. edsl/jobs/tasks/TaskHistory.py +3 -4
  25. edsl/language_models/LanguageModel.py +11 -5
  26. edsl/language_models/ModelList.py +1 -1
  27. edsl/language_models/repair.py +7 -8
  28. edsl/notebooks/Notebook.py +3 -40
  29. edsl/prompts/Prompt.py +19 -31
  30. edsl/questions/QuestionBase.py +13 -38
  31. edsl/questions/QuestionBudget.py +6 -5
  32. edsl/questions/QuestionCheckBox.py +3 -7
  33. edsl/questions/QuestionExtract.py +3 -5
  34. edsl/questions/QuestionFreeText.py +3 -3
  35. edsl/questions/QuestionFunctional.py +3 -0
  36. edsl/questions/QuestionList.py +4 -3
  37. edsl/questions/QuestionMultipleChoice.py +8 -16
  38. edsl/questions/QuestionNumerical.py +3 -4
  39. edsl/questions/QuestionRank.py +3 -5
  40. edsl/questions/__init__.py +3 -4
  41. edsl/questions/descriptors.py +2 -4
  42. edsl/questions/question_registry.py +31 -20
  43. edsl/questions/settings.py +1 -1
  44. edsl/results/Dataset.py +0 -31
  45. edsl/results/Result.py +74 -22
  46. edsl/results/Results.py +47 -97
  47. edsl/results/ResultsDBMixin.py +3 -7
  48. edsl/results/ResultsExportMixin.py +537 -22
  49. edsl/results/ResultsGGMixin.py +3 -3
  50. edsl/results/ResultsToolsMixin.py +5 -5
  51. edsl/scenarios/Scenario.py +6 -5
  52. edsl/scenarios/ScenarioList.py +11 -34
  53. edsl/scenarios/ScenarioListPdfMixin.py +1 -2
  54. edsl/scenarios/__init__.py +0 -1
  55. edsl/study/Study.py +9 -3
  56. edsl/surveys/MemoryPlan.py +4 -11
  57. edsl/surveys/Survey.py +7 -46
  58. edsl/surveys/SurveyExportMixin.py +2 -4
  59. edsl/surveys/SurveyFlowVisualizationMixin.py +4 -6
  60. edsl/tools/plotting.py +2 -4
  61. edsl/utilities/__init__.py +21 -21
  62. edsl/utilities/interface.py +45 -66
  63. edsl/utilities/utilities.py +13 -11
  64. {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.dist-info}/METADATA +10 -11
  65. {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.dist-info}/RECORD +68 -71
  66. edsl-0.1.29.dev2.dist-info/entry_points.txt +3 -0
  67. edsl/base/Base.py +0 -289
  68. edsl/results/DatasetExportMixin.py +0 -493
  69. edsl/scenarios/FileStore.py +0 -140
  70. edsl/scenarios/ScenarioListExportMixin.py +0 -32
  71. {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.dist-info}/LICENSE +0 -0
  72. {edsl-0.1.29.dist-info → edsl-0.1.29.dev2.dist-info}/WHEEL +0 -0
@@ -1,376 +1,134 @@
1
- from typing import Dict, Any, Optional
2
- from collections import UserList
1
+ from typing import Dict, Any
3
2
 
4
- # from functools import reduce
5
3
  from edsl.prompts.Prompt import Prompt
6
-
7
- # from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
4
+ from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
8
5
  from edsl.prompts.registry import get_classes as prompt_lookup
9
6
  from edsl.exceptions import QuestionScenarioRenderError
10
7
 
11
- import enum
12
-
13
-
14
- class PromptComponent(enum.Enum):
15
- AGENT_INSTRUCTIONS = "agent_instructions"
16
- AGENT_PERSONA = "agent_persona"
17
- QUESTION_INSTRUCTIONS = "question_instructions"
18
- PRIOR_QUESTION_MEMORY = "prior_question_memory"
19
-
20
-
21
- class PromptList(UserList):
22
- separator = Prompt(" ")
23
-
24
- def reduce(self):
25
- """Reduce the list of prompts to a single prompt.
26
-
27
- >>> p = PromptList([Prompt("You are a happy-go lucky agent."), Prompt("You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}")])
28
- >>> p.reduce()
29
- Prompt(text=\"""You are a happy-go lucky agent. You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}\""")
30
-
31
- """
32
- p = self[0]
33
- for prompt in self[1:]:
34
- if len(prompt) > 0:
35
- p = p + self.separator + prompt
36
- return p
37
-
38
-
39
- class PromptPlan:
40
- """A plan for constructing prompts for the LLM call.
41
- Every prompt plan has a user prompt order and a system prompt order.
42
- It must contain each of the values in the PromptComponent enum.
43
-
44
-
45
- >>> p = PromptPlan(user_prompt_order=(PromptComponent.AGENT_INSTRUCTIONS, PromptComponent.AGENT_PERSONA),system_prompt_order=(PromptComponent.QUESTION_INSTRUCTIONS, PromptComponent.PRIOR_QUESTION_MEMORY))
46
- >>> p._is_valid_plan()
47
- True
48
-
49
- >>> p.arrange_components(agent_instructions=1, agent_persona=2, question_instructions=3, prior_question_memory=4)
50
- {'user_prompt': ..., 'system_prompt': ...}
51
-
52
- >>> p = PromptPlan(user_prompt_order=("agent_instructions", ), system_prompt_order=("question_instructions", "prior_question_memory"))
53
- Traceback (most recent call last):
54
- ...
55
- ValueError: Invalid plan: must contain each value of PromptComponent exactly once.
56
-
57
- """
58
-
59
- def __init__(
60
- self,
61
- user_prompt_order: Optional[tuple] = None,
62
- system_prompt_order: Optional[tuple] = None,
63
- ):
64
- """Initialize the PromptPlan."""
65
-
66
- if user_prompt_order is None:
67
- user_prompt_order = (
68
- PromptComponent.QUESTION_INSTRUCTIONS,
69
- PromptComponent.PRIOR_QUESTION_MEMORY,
70
- )
71
- if system_prompt_order is None:
72
- system_prompt_order = (
73
- PromptComponent.AGENT_INSTRUCTIONS,
74
- PromptComponent.AGENT_PERSONA,
75
- )
76
-
77
- # very commmon way to screw this up given how python treats single strings as iterables
78
- if isinstance(user_prompt_order, str):
79
- user_prompt_order = (user_prompt_order,)
80
8
 
81
- if isinstance(system_prompt_order, str):
82
- system_prompt_order = (system_prompt_order,)
83
-
84
- if not isinstance(user_prompt_order, tuple):
85
- raise TypeError(
86
- f"Expected a tuple, but got {type(user_prompt_order).__name__}"
87
- )
88
-
89
- if not isinstance(system_prompt_order, tuple):
90
- raise TypeError(
91
- f"Expected a tuple, but got {type(system_prompt_order).__name__}"
92
- )
93
-
94
- self.user_prompt_order = self._convert_to_enum(user_prompt_order)
95
- self.system_prompt_order = self._convert_to_enum(system_prompt_order)
96
- if not self._is_valid_plan():
97
- raise ValueError(
98
- "Invalid plan: must contain each value of PromptComponent exactly once."
99
- )
100
-
101
- def _convert_to_enum(self, prompt_order: tuple):
102
- """Convert string names to PromptComponent enum values."""
103
- return tuple(
104
- PromptComponent(component) if isinstance(component, str) else component
105
- for component in prompt_order
106
- )
107
-
108
- def _is_valid_plan(self):
109
- """Check if the plan is valid."""
110
- combined = self.user_prompt_order + self.system_prompt_order
111
- return set(combined) == set(PromptComponent)
9
+ class PromptConstructorMixin:
10
+ def construct_system_prompt(self) -> Prompt:
11
+ """Construct the system prompt for the LLM call."""
112
12
 
113
- def arrange_components(self, **kwargs) -> Dict[PromptComponent, Prompt]:
114
- """Arrange the components in the order specified by the plan."""
115
- # check is valid components passed
116
- component_strings = set([pc.value for pc in PromptComponent])
117
- if not set(kwargs.keys()) == component_strings:
118
- raise ValueError(
119
- f"Invalid components passed: {set(kwargs.keys())} but expected {PromptComponent}"
120
- )
13
+ agent_instructions = self._get_agent_instructions_prompt()
14
+ persona_prompt = self._get_persona_prompt()
121
15
 
122
- user_prompt = PromptList(
123
- [kwargs[component.value] for component in self.user_prompt_order]
124
- )
125
- system_prompt = PromptList(
126
- [kwargs[component.value] for component in self.system_prompt_order]
16
+ return (
17
+ agent_instructions
18
+ + " " * int(len(persona_prompt.text) > 0)
19
+ + persona_prompt
127
20
  )
128
- return {"user_prompt": user_prompt, "system_prompt": system_prompt}
129
-
130
- def get_prompts(self, **kwargs) -> Dict[str, Prompt]:
131
- """Get both prompts for the LLM call."""
132
- prompts = self.arrange_components(**kwargs)
133
- return {
134
- "user_prompt": prompts["user_prompt"].reduce(),
135
- "system_prompt": prompts["system_prompt"].reduce(),
136
- }
137
-
138
-
139
- class PromptConstructorMixin:
140
- """Mixin for constructing prompts for the LLM call.
141
-
142
- The pieces of a prompt are:
143
- - The agent instructions - "You are answering questions as if you were a human. Do not break character."
144
- - The persona prompt - "You are an agent with the following persona: {'age': 22, 'hair': 'brown', 'height': 5.5}"
145
- - The question instructions - "You are being asked the following question: Do you like school? The options are 0: yes 1: no Return a valid JSON formatted like this, selecting only the number of the option: {"answer": <put answer code here>, "comment": "<put explanation here>"} Only 1 option may be selected."
146
- - The memory prompt - "Before the question you are now answering, you already answered the following question(s): Question: Do you like school? Answer: Prior answer"
147
21
 
148
- This is mixed into the Invigilator class.
149
- """
22
+ def _get_persona_prompt(self) -> Prompt:
23
+ """Get the persona prompt.
150
24
 
151
- prompt_plan = PromptPlan()
25
+ The is the description of the agent to the LLM.
152
26
 
153
- @property
154
- def agent_instructions_prompt(self) -> Prompt:
27
+ The agent_persona is constructed when the Agent is created.
28
+ If the agent is passed a template for "agent_trait_presentation_template" that is used to construct the persona.
29
+ If it does not exist, the persona is looked up in the prompt registry
155
30
  """
156
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
157
- >>> i = InvigilatorBase.example()
158
- >>> i.agent_instructions_prompt
159
- Prompt(text=\"""You are answering questions as if you were a human. Do not break character.\""")
160
- """
161
- if not hasattr(self, "_agent_instructions_prompt"):
31
+ if not hasattr(self.agent, "agent_persona"):
162
32
  applicable_prompts = prompt_lookup(
163
- component_type="agent_instructions",
33
+ component_type="agent_persona",
164
34
  model=self.model.model,
165
35
  )
166
- if len(applicable_prompts) == 0:
167
- raise Exception("No applicable prompts found")
168
- self._agent_instructions_prompt = applicable_prompts[0](
169
- text=self.agent.instruction
36
+ persona_prompt_template = applicable_prompts[0]()
37
+ else:
38
+ persona_prompt_template = self.agent.agent_persona
39
+
40
+ # TODO: This multiple passing of agent traits - not sure if it is necessary. Not harmful.
41
+ if undefined := persona_prompt_template.undefined_template_variables(
42
+ self.agent.traits
43
+ | {"traits": self.agent.traits}
44
+ | {"codebook": self.agent.codebook}
45
+ | {"traits": self.agent.traits}
46
+ ):
47
+ raise QuestionScenarioRenderError(
48
+ f"Agent persona still has variables that were not rendered: {undefined}"
170
49
  )
171
- return self._agent_instructions_prompt
172
-
173
- @property
174
- def agent_persona_prompt(self) -> Prompt:
175
- """
176
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
177
- >>> i = InvigilatorBase.example()
178
- >>> i.agent_persona_prompt
179
- Prompt(text=\"""You are an agent with the following persona:
180
- {'age': 22, 'hair': 'brown', 'height': 5.5}\""")
181
-
182
- """
183
- if not hasattr(self, "_agent_persona_prompt"):
184
- if not hasattr(self.agent, "agent_persona"):
185
- applicable_prompts = prompt_lookup(
186
- component_type="agent_persona",
187
- model=self.model.model,
188
- )
189
- persona_prompt_template = applicable_prompts[0]()
190
- else:
191
- persona_prompt_template = self.agent.agent_persona
192
-
193
- # TODO: This multiple passing of agent traits - not sure if it is necessary. Not harmful.
194
- if undefined := persona_prompt_template.undefined_template_variables(
195
- self.agent.traits
196
- | {"traits": self.agent.traits}
197
- | {"codebook": self.agent.codebook}
198
- | {"traits": self.agent.traits}
199
- ):
200
- raise QuestionScenarioRenderError(
201
- f"Agent persona still has variables that were not rendered: {undefined}"
202
- )
203
-
204
- persona_prompt = persona_prompt_template.render(
205
- self.agent.traits | {"traits": self.agent.traits},
206
- codebook=self.agent.codebook,
207
- traits=self.agent.traits,
208
- )
209
- if persona_prompt.has_variables:
210
- raise QuestionScenarioRenderError(
211
- "Agent persona still has variables that were not rendered."
212
- )
213
- self._agent_persona_prompt = persona_prompt
214
-
215
- return self._agent_persona_prompt
216
-
217
- @property
218
- def question_instructions_prompt(self) -> Prompt:
219
- """
220
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
221
- >>> i = InvigilatorBase.example()
222
- >>> i.question_instructions_prompt
223
- Prompt(text=\"""You are being asked the following question: Do you like school?
224
- The options are
225
- <BLANKLINE>
226
- 0: yes
227
- <BLANKLINE>
228
- 1: no
229
- <BLANKLINE>
230
- Return a valid JSON formatted like this, selecting only the number of the option:
231
- {"answer": <put answer code here>, "comment": "<put explanation here>"}
232
- Only 1 option may be selected.\""")
233
50
 
234
- >>> from edsl import QuestionFreeText
235
- >>> q = QuestionFreeText(question_text = "Consider {{ X }}. What is your favorite color?", question_name = "q_color")
236
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
237
- >>> i = InvigilatorBase.example(question = q)
238
- >>> i.question_instructions_prompt
239
- Traceback (most recent call last):
240
- ...
241
- edsl.exceptions.questions.QuestionScenarioRenderError: Question instructions still has variables: ['X'].
242
-
243
-
244
- >>> from edsl import QuestionFreeText
245
- >>> q = QuestionFreeText(question_text = "You were asked the question '{{ q0.question_text }}'. What is your favorite color?", question_name = "q_color")
246
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
247
- >>> i = InvigilatorBase.example(question = q)
248
- >>> i.question_instructions_prompt
249
- Prompt(text=\"""You are being asked the following question: You were asked the question 'Do you like school?'. What is your favorite color?
250
- Return a valid JSON formatted like this:
251
- {"answer": "<put free text answer here>"}\""")
252
-
253
- >>> from edsl import QuestionFreeText
254
- >>> q = QuestionFreeText(question_text = "You stated '{{ q0.answer }}'. What is your favorite color?", question_name = "q_color")
255
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
256
- >>> i = InvigilatorBase.example(question = q)
257
- >>> i.current_answers = {"q0": "I like school"}
258
- >>> i.question_instructions_prompt
259
- Prompt(text=\"""You are being asked the following question: You stated 'I like school'. What is your favorite color?
260
- Return a valid JSON formatted like this:
261
- {"answer": "<put free text answer here>"}\""")
262
-
263
-
264
- """
265
- if not hasattr(self, "_question_instructions_prompt"):
266
- question_prompt = self.question.get_instructions(model=self.model.model)
267
-
268
- # TODO: Try to populate the answers in the question object if they are available
269
- d = self.survey.question_names_to_questions()
270
- for question, answer in self.current_answers.items():
271
- if question in d:
272
- d[question].answer = answer
273
- else:
274
- # adds a comment to the question
275
- if (new_question := question.split("_comment")[0]) in d:
276
- d[new_question].comment = answer
51
+ persona_prompt = persona_prompt_template.render(
52
+ self.agent.traits | {"traits": self.agent.traits},
53
+ codebook=self.agent.codebook,
54
+ traits=self.agent.traits,
55
+ )
277
56
 
278
- rendered_instructions = question_prompt.render(
279
- self.question.data | self.scenario | d | {"agent": self.agent}
57
+ if persona_prompt.has_variables:
58
+ raise QuestionScenarioRenderError(
59
+ "Agent persona still has variables that were not rendered."
280
60
  )
61
+ return persona_prompt
281
62
 
282
- undefined_template_variables = (
283
- rendered_instructions.undefined_template_variables({})
63
+ def _get_agent_instructions_prompt(self) -> Prompt:
64
+ """Get the agent instructions prompt."""
65
+ applicable_prompts = prompt_lookup(
66
+ component_type="agent_instructions",
67
+ model=self.model.model,
68
+ )
69
+ if len(applicable_prompts) == 0:
70
+ raise Exception("No applicable prompts found")
71
+ return applicable_prompts[0](text=self.agent.instruction)
72
+
73
+ def _get_question_instructions(self) -> Prompt:
74
+ """Get the instructions for the question."""
75
+ # applicable_prompts = prompt_lookup(
76
+ # component_type="question_instructions",
77
+ # question_type=self.question.question_type,
78
+ # model=self.model.model,
79
+ # )
80
+ ## Get the question instructions and renders with the scenario & question.data
81
+ # question_prompt = applicable_prompts[0]()
82
+ question_prompt = self.question.get_instructions(model=self.model.model)
83
+
84
+ undefined_template_variables = question_prompt.undefined_template_variables(
85
+ self.question.data | self.scenario
86
+ )
87
+ if undefined_template_variables:
88
+ print(undefined_template_variables)
89
+ raise QuestionScenarioRenderError(
90
+ "Question instructions still has variables."
284
91
  )
285
92
 
286
- # Check if it's the name of a question in the survey
287
- for question_name in self.survey.question_names:
288
- if question_name in undefined_template_variables:
289
- print(
290
- "Question name found in undefined_template_variables: ",
291
- question_name,
292
- )
293
-
294
- if undefined_template_variables:
295
- print(undefined_template_variables)
296
- raise QuestionScenarioRenderError(
297
- f"Question instructions still has variables: {undefined_template_variables}."
298
- )
299
-
300
- self._question_instructions_prompt = rendered_instructions
301
- return self._question_instructions_prompt
302
-
303
- @property
304
- def prior_question_memory_prompt(self) -> Prompt:
305
- if not hasattr(self, "_prior_question_memory_prompt"):
306
- from edsl.prompts.Prompt import Prompt
307
-
308
- memory_prompt = Prompt(text="")
309
- if self.memory_plan is not None:
310
- memory_prompt += self.create_memory_prompt(
311
- self.question.question_name
312
- ).render(self.scenario)
313
- self._prior_question_memory_prompt = memory_prompt
314
- return self._prior_question_memory_prompt
315
-
316
- def construct_system_prompt(self) -> Prompt:
317
- """Construct the system prompt for the LLM call."""
318
- import warnings
319
-
320
- warnings.warn(
321
- "This method is deprecated. Use get_prompts instead.", DeprecationWarning
322
- )
323
- return self.get_prompts()["system_prompt"]
93
+ return question_prompt.render(self.question.data | self.scenario)
324
94
 
325
95
  def construct_user_prompt(self) -> Prompt:
326
96
  """Construct the user prompt for the LLM call."""
327
- import warnings
328
-
329
- warnings.warn(
330
- "This method is deprecated. Use get_prompts instead.", DeprecationWarning
331
- )
332
- return self.get_prompts()["user_prompt"]
97
+ user_prompt = self._get_question_instructions()
98
+ if self.memory_plan is not None:
99
+ user_prompt += self.create_memory_prompt(
100
+ self.question.question_name
101
+ ).render(self.scenario)
102
+ return user_prompt
333
103
 
334
104
  def get_prompts(self) -> Dict[str, Prompt]:
335
- """Get both prompts for the LLM call.
336
-
337
- >>> from edsl import QuestionFreeText
338
- >>> from edsl.agents.InvigilatorBase import InvigilatorBase
339
- >>> q = QuestionFreeText(question_text="How are you today?", question_name="q0")
340
- >>> i = InvigilatorBase.example(question = q)
341
- >>> i.get_prompts()
342
- {'user_prompt': ..., 'system_prompt': ...}
343
- >>> scenario = i._get_scenario_with_image()
344
- >>> scenario.has_image
345
- True
346
- >>> q = QuestionFreeText(question_text="How are you today?", question_name="q0")
347
- >>> i = InvigilatorBase.example(question = q, scenario = scenario)
348
- >>> i.get_prompts()
349
- {'user_prompt': ..., 'system_prompt': ..., 'encoded_image': ...'}
350
- """
351
- prompts = self.prompt_plan.get_prompts(
352
- agent_instructions=self.agent_instructions_prompt,
353
- agent_persona=self.agent_persona_prompt,
354
- question_instructions=self.question_instructions_prompt,
355
- prior_question_memory=self.prior_question_memory_prompt,
356
- )
357
-
105
+ """Get both prompts for the LLM call."""
106
+ system_prompt = self.construct_system_prompt()
107
+ user_prompt = self.construct_user_prompt()
108
+ prompts = {
109
+ "user_prompt": user_prompt,
110
+ "system_prompt": system_prompt,
111
+ }
358
112
  if hasattr(self.scenario, "has_image") and self.scenario.has_image:
359
113
  prompts["encoded_image"] = self.scenario["encoded_image"]
360
114
  return prompts
361
115
 
362
- def _get_scenario_with_image(self) -> Dict[str, Any]:
363
- """This is a helper function to get a scenario with an image, for testing purposes."""
364
- from edsl import Scenario
365
-
366
- try:
367
- scenario = Scenario.from_image("../../static/logo.png")
368
- except FileNotFoundError:
369
- scenario = Scenario.from_image("static/logo.png")
370
- return scenario
371
-
372
116
 
373
117
  if __name__ == "__main__":
374
- import doctest
375
-
376
- doctest.testmod(optionflags=doctest.ELLIPSIS)
118
+ from edsl import Model
119
+ from edsl import Agent
120
+
121
+ a = Agent(
122
+ instruction="You are a happy-go lucky agent.",
123
+ traits={"feeling": "happy", "age": "Young at heart"},
124
+ codebook={"feeling": "Feelings right now", "age": "Age in years"},
125
+ trait_presentation_template="",
126
+ )
127
+ p = PromptConstructorMixin()
128
+ p.model = Model(Model.available()[0])
129
+ p.agent = a
130
+ instructions = p._get_agent_instructions_prompt()
131
+ repr(instructions)
132
+
133
+ persona = p._get_persona_prompt()
134
+ repr(persona)
@@ -1,6 +1,7 @@
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
4
5
  from edsl.exceptions.agents import AgentNameError, AgentTraitKeyError
5
6
 
6
7
 
@@ -29,8 +30,6 @@ class TraitsDescriptor:
29
30
 
30
31
  def __set__(self, instance, traits_dict: Dict[str, str]) -> None:
31
32
  """Set the value of the attribute."""
32
- from edsl.utilities.utilities import is_valid_variable_name
33
-
34
33
  for key, value in traits_dict.items():
35
34
  if key == "name":
36
35
  raise AgentNameError(
edsl/config.py CHANGED
@@ -1,11 +1,11 @@
1
1
  """This module provides a Config class that loads environment variables from a .env file and sets them as class attributes."""
2
2
 
3
3
  import os
4
+ from dotenv import load_dotenv, find_dotenv
4
5
  from edsl.exceptions import (
5
6
  InvalidEnvironmentVariableError,
6
7
  MissingEnvironmentVariableError,
7
8
  )
8
- from dotenv import load_dotenv, find_dotenv
9
9
 
10
10
  # valid values for EDSL_RUN_MODE
11
11
  EDSL_RUN_MODES = ["development", "development-testrun", "production"]
@@ -96,7 +96,6 @@ class Config:
96
96
  Loads the .env
97
97
  - Overrides existing env vars unless EDSL_RUN_MODE=="development-testrun"
98
98
  """
99
-
100
99
  override = True
101
100
  if self.EDSL_RUN_MODE == "development-testrun":
102
101
  override = False
edsl/conjure/InputData.py CHANGED
@@ -1,4 +1,5 @@
1
- import base64
1
+ import functools
2
+
2
3
  from abc import ABC, abstractmethod
3
4
  from typing import Dict, Callable, Optional, List, Generator, Tuple, Union
4
5
  from collections import namedtuple
@@ -51,7 +52,6 @@ class InputDataABC(
51
52
  config: Optional[dict] = None,
52
53
  naming_function: Optional[Callable] = sanitize_string,
53
54
  raw_data: Optional[List] = None,
54
- binary: Optional[str] = None,
55
55
  question_names: Optional[List[str]] = None,
56
56
  question_texts: Optional[List[str]] = None,
57
57
  answer_codebook: Optional[Dict] = None,
@@ -83,15 +83,6 @@ class InputDataABC(
83
83
  self.config = config
84
84
  self.naming_function = naming_function
85
85
 
86
- if binary is not None:
87
- self.binary = binary
88
- else:
89
- try:
90
- with open(self.datafile_name, "rb") as file:
91
- self.binary = base64.b64encode(file.read()).decode()
92
- except FileNotFoundError:
93
- self.binary = None
94
-
95
86
  def default_repair_func(x):
96
87
  return (
97
88
  x.replace("#", "_num")
@@ -127,14 +118,6 @@ class InputDataABC(
127
118
  if order_options:
128
119
  self.order_options()
129
120
 
130
- @property
131
- def download_link(self):
132
- from IPython.display import HTML
133
-
134
- actual_file_name = self.datafile_name.split("/")[-1]
135
- download_link = f'<a href="data:text/plain;base64,{self.binary}" download="{actual_file_name}">Download {self.datafile_name}</a>'
136
- return HTML(download_link)
137
-
138
121
  @abstractmethod
139
122
  def get_question_texts(self) -> List[str]:
140
123
  """Get the text of the questions
@@ -168,9 +151,7 @@ class InputDataABC(
168
151
  """
169
152
  raise NotImplementedError
170
153
 
171
- def rename_questions(
172
- self, rename_dict: Dict[str, str], ignore_missing=False
173
- ) -> "InputData":
154
+ def rename_questions(self, rename_dict: Dict[str, str]) -> "InputData":
174
155
  """Rename a question.
175
156
 
176
157
  >>> id = InputDataABC.example()
@@ -179,10 +160,10 @@ class InputDataABC(
179
160
 
180
161
  """
181
162
  for old_name, new_name in rename_dict.items():
182
- self.rename(old_name, new_name, ignore_missing=ignore_missing)
163
+ self.rename(old_name, new_name)
183
164
  return self
184
165
 
185
- def rename(self, old_name, new_name, ignore_missing=False) -> "InputData":
166
+ def rename(self, old_name, new_name) -> "InputData":
186
167
  """Rename a question.
187
168
 
188
169
  >>> id = InputDataABC.example()
@@ -190,19 +171,13 @@ class InputDataABC(
190
171
  ['evening', 'feeling']
191
172
 
192
173
  """
193
- if old_name not in self.question_names:
194
- if ignore_missing:
195
- return self
196
- else:
197
- raise ValueError(f"Question {old_name} not found.")
198
-
199
174
  idx = self.question_names.index(old_name)
200
175
  self.question_names[idx] = new_name
201
176
  self.answer_codebook[new_name] = self.answer_codebook.pop(old_name, {})
202
177
 
203
178
  return self
204
179
 
205
- def _drop_question(self, question_name, ignore_missing=False):
180
+ def _drop_question(self, question_name):
206
181
  """Drop a question
207
182
 
208
183
  >>> id = InputDataABC.example()
@@ -210,11 +185,6 @@ class InputDataABC(
210
185
  ['feeling']
211
186
 
212
187
  """
213
- if question_name not in self.question_names:
214
- if ignore_missing:
215
- return self
216
- else:
217
- raise ValueError(f"Question {question_name} not found.")
218
188
  idx = self.question_names.index(question_name)
219
189
  self._question_names.pop(idx)
220
190
  self._question_texts.pop(idx)
@@ -236,7 +206,7 @@ class InputDataABC(
236
206
  self._drop_question(qn)
237
207
  return self
238
208
 
239
- def keep(self, *question_names_to_keep, ignore_missing=False) -> "InputDataABC":
209
+ def keep(self, *question_names_to_keep) -> "InputDataABC":
240
210
  """Keep a question.
241
211
 
242
212
  >>> id = InputDataABC.example()
@@ -247,7 +217,7 @@ class InputDataABC(
247
217
  all_question_names = self._question_names[:]
248
218
  for qn in all_question_names:
249
219
  if qn not in question_names_to_keep:
250
- self._drop_question(qn, ignore_missing=ignore_missing)
220
+ self._drop_question(qn)
251
221
  return self
252
222
 
253
223
  def modify_question_type(
@@ -314,7 +284,6 @@ class InputDataABC(
314
284
  "raw_data": self.raw_data,
315
285
  "question_names": self.question_names,
316
286
  "question_texts": self.question_texts,
317
- "binary": self.binary,
318
287
  "answer_codebook": self.answer_codebook,
319
288
  "question_types": self.question_types,
320
289
  }