edsl 0.1.29.dev3__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.
- edsl/Base.py +18 -18
- edsl/__init__.py +23 -23
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +79 -41
- edsl/agents/AgentList.py +26 -26
- edsl/agents/Invigilator.py +19 -2
- edsl/agents/InvigilatorBase.py +15 -10
- edsl/agents/PromptConstructionMixin.py +342 -100
- edsl/agents/descriptors.py +2 -1
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/conjure/InputData.py +39 -8
- edsl/conversation/car_buying.py +1 -1
- edsl/coop/coop.py +187 -150
- edsl/coop/utils.py +43 -75
- edsl/data/Cache.py +41 -18
- edsl/data/CacheEntry.py +6 -7
- edsl/data/SQLiteDict.py +11 -3
- edsl/data_transfer_models.py +4 -0
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +108 -49
- edsl/jobs/buckets/ModelBuckets.py +14 -2
- edsl/jobs/buckets/TokenBucket.py +32 -5
- edsl/jobs/interviews/Interview.py +99 -79
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +19 -24
- edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
- edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +17 -17
- edsl/language_models/ModelList.py +1 -1
- edsl/language_models/repair.py +8 -7
- edsl/notebooks/Notebook.py +47 -10
- edsl/prompts/Prompt.py +31 -19
- edsl/questions/QuestionBase.py +38 -13
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +7 -5
- edsl/questions/QuestionFunctional.py +34 -5
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +68 -12
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +46 -4
- edsl/questions/question_registry.py +20 -31
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +31 -0
- edsl/results/DatasetExportMixin.py +570 -0
- edsl/results/Result.py +66 -70
- edsl/results/Results.py +160 -68
- edsl/results/ResultsDBMixin.py +7 -3
- edsl/results/ResultsExportMixin.py +22 -537
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +5 -5
- edsl/scenarios/FileStore.py +299 -0
- edsl/scenarios/Scenario.py +16 -24
- edsl/scenarios/ScenarioList.py +42 -17
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/study/Study.py +8 -16
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +88 -17
- edsl/surveys/SurveyExportMixin.py +4 -2
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/tools/plotting.py +4 -2
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +66 -45
- edsl/utilities/utilities.py +11 -13
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/METADATA +11 -10
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/RECORD +74 -71
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
- edsl-0.1.29.dev3.dist-info/entry_points.txt +0 -3
- {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/LICENSE +0 -0
@@ -1,134 +1,376 @@
|
|
1
|
-
from typing import Dict, Any
|
1
|
+
from typing import Dict, Any, Optional
|
2
|
+
from collections import UserList
|
2
3
|
|
4
|
+
# from functools import reduce
|
3
5
|
from edsl.prompts.Prompt import Prompt
|
4
|
-
|
6
|
+
|
7
|
+
# from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
|
5
8
|
from edsl.prompts.registry import get_classes as prompt_lookup
|
6
9
|
from edsl.exceptions import QuestionScenarioRenderError
|
7
10
|
|
11
|
+
import enum
|
8
12
|
|
9
|
-
class PromptConstructorMixin:
|
10
|
-
def construct_system_prompt(self) -> Prompt:
|
11
|
-
"""Construct the system prompt for the LLM call."""
|
12
13
|
|
13
|
-
|
14
|
-
|
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"
|
15
19
|
|
16
|
-
return (
|
17
|
-
agent_instructions
|
18
|
-
+ " " * int(len(persona_prompt.text) > 0)
|
19
|
-
+ persona_prompt
|
20
|
-
)
|
21
20
|
|
22
|
-
|
23
|
-
|
21
|
+
class PromptList(UserList):
|
22
|
+
separator = Prompt(" ")
|
23
|
+
|
24
|
+
def reduce(self):
|
25
|
+
"""Reduce the list of prompts to a single prompt.
|
24
26
|
|
25
|
-
|
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}\""")
|
26
30
|
|
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
|
30
31
|
"""
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
+
|
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__}"
|
35
92
|
)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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}"
|
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."
|
49
99
|
)
|
50
100
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
55
106
|
)
|
56
107
|
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
112
|
+
|
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}"
|
60
120
|
)
|
61
|
-
return persona_prompt
|
62
121
|
|
63
|
-
|
64
|
-
|
65
|
-
applicable_prompts = prompt_lookup(
|
66
|
-
component_type="agent_instructions",
|
67
|
-
model=self.model.model,
|
122
|
+
user_prompt = PromptList(
|
123
|
+
[kwargs[component.value] for component in self.user_prompt_order]
|
68
124
|
)
|
69
|
-
|
70
|
-
|
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
|
125
|
+
system_prompt = PromptList(
|
126
|
+
[kwargs[component.value] for component in self.system_prompt_order]
|
86
127
|
)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
+
|
148
|
+
This is mixed into the Invigilator class.
|
149
|
+
"""
|
150
|
+
|
151
|
+
prompt_plan = PromptPlan()
|
152
|
+
|
153
|
+
@property
|
154
|
+
def agent_instructions_prompt(self) -> Prompt:
|
155
|
+
"""
|
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"):
|
162
|
+
applicable_prompts = prompt_lookup(
|
163
|
+
component_type="agent_instructions",
|
164
|
+
model=self.model.model,
|
91
165
|
)
|
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
|
170
|
+
)
|
171
|
+
return self._agent_instructions_prompt
|
92
172
|
|
93
|
-
|
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
|
+
|
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
|
277
|
+
|
278
|
+
rendered_instructions = question_prompt.render(
|
279
|
+
self.question.data | self.scenario | d | {"agent": self.agent}
|
280
|
+
)
|
281
|
+
|
282
|
+
undefined_template_variables = (
|
283
|
+
rendered_instructions.undefined_template_variables({})
|
284
|
+
)
|
285
|
+
|
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"]
|
94
324
|
|
95
325
|
def construct_user_prompt(self) -> Prompt:
|
96
326
|
"""Construct the user prompt for the LLM call."""
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
return user_prompt
|
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"]
|
103
333
|
|
104
334
|
def get_prompts(self) -> Dict[str, Prompt]:
|
105
|
-
"""Get both prompts for the LLM call.
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
+
|
112
358
|
if hasattr(self.scenario, "has_image") and self.scenario.has_image:
|
113
359
|
prompts["encoded_image"] = self.scenario["encoded_image"]
|
114
360
|
return prompts
|
115
361
|
|
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
|
+
|
116
372
|
|
117
373
|
if __name__ == "__main__":
|
118
|
-
|
119
|
-
|
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)
|
374
|
+
import doctest
|
375
|
+
|
376
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/agents/descriptors.py
CHANGED
@@ -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(
|