edsl 0.1.39__py3-none-any.whl → 0.1.39.dev1__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 +116 -197
- edsl/__init__.py +7 -15
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +147 -351
- edsl/agents/AgentList.py +73 -211
- edsl/agents/Invigilator.py +50 -101
- edsl/agents/InvigilatorBase.py +70 -62
- edsl/agents/PromptConstructor.py +225 -143
- edsl/agents/__init__.py +1 -0
- edsl/agents/prompt_helpers.py +3 -3
- edsl/auto/AutoStudy.py +5 -18
- edsl/auto/StageBase.py +40 -53
- edsl/auto/StageQuestions.py +1 -2
- edsl/auto/utilities.py +6 -0
- edsl/config.py +2 -22
- edsl/conversation/car_buying.py +1 -2
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +47 -125
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +27 -45
- edsl/data/CacheEntry.py +15 -12
- edsl/data/CacheHandler.py +12 -31
- edsl/data/RemoteCacheSync.py +46 -154
- edsl/data/__init__.py +3 -4
- edsl/data_transfer_models.py +1 -2
- edsl/enums.py +0 -27
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +0 -12
- edsl/exceptions/questions.py +6 -24
- edsl/exceptions/scenarios.py +0 -7
- edsl/inference_services/AnthropicService.py +19 -38
- edsl/inference_services/AwsBedrock.py +2 -0
- edsl/inference_services/AzureAI.py +2 -0
- edsl/inference_services/GoogleService.py +12 -7
- edsl/inference_services/InferenceServiceABC.py +85 -18
- edsl/inference_services/InferenceServicesCollection.py +79 -120
- edsl/inference_services/MistralAIService.py +3 -0
- edsl/inference_services/OpenAIService.py +35 -47
- edsl/inference_services/PerplexityService.py +3 -0
- edsl/inference_services/TestService.py +10 -11
- edsl/inference_services/TogetherAIService.py +3 -5
- edsl/jobs/Answers.py +14 -1
- edsl/jobs/Jobs.py +431 -356
- edsl/jobs/JobsChecks.py +10 -35
- edsl/jobs/JobsPrompts.py +4 -6
- edsl/jobs/JobsRemoteInferenceHandler.py +133 -205
- edsl/jobs/buckets/BucketCollection.py +3 -44
- edsl/jobs/buckets/TokenBucket.py +21 -53
- edsl/jobs/interviews/Interview.py +408 -143
- edsl/jobs/runners/JobsRunnerAsyncio.py +403 -88
- edsl/jobs/runners/JobsRunnerStatus.py +165 -133
- edsl/jobs/tasks/QuestionTaskCreator.py +19 -21
- edsl/jobs/tasks/TaskHistory.py +18 -38
- edsl/jobs/tasks/task_status_enum.py +2 -0
- edsl/language_models/KeyLookup.py +30 -0
- edsl/language_models/LanguageModel.py +236 -194
- edsl/language_models/ModelList.py +19 -28
- edsl/language_models/__init__.py +2 -1
- edsl/language_models/registry.py +190 -0
- edsl/language_models/repair.py +2 -2
- edsl/language_models/unused/ReplicateBase.py +83 -0
- edsl/language_models/utilities.py +4 -5
- edsl/notebooks/Notebook.py +14 -19
- edsl/prompts/Prompt.py +39 -29
- edsl/questions/{answer_validator_mixin.py → AnswerValidatorMixin.py} +2 -47
- edsl/questions/QuestionBase.py +214 -68
- edsl/questions/{question_base_gen_mixin.py → QuestionBaseGenMixin.py} +50 -57
- edsl/questions/QuestionBasePromptsMixin.py +3 -7
- edsl/questions/QuestionBudget.py +1 -1
- edsl/questions/QuestionCheckBox.py +3 -3
- edsl/questions/QuestionExtract.py +7 -5
- edsl/questions/QuestionFreeText.py +3 -2
- edsl/questions/QuestionList.py +18 -10
- edsl/questions/QuestionMultipleChoice.py +23 -67
- edsl/questions/QuestionNumerical.py +4 -2
- edsl/questions/QuestionRank.py +17 -7
- edsl/questions/{response_validator_abc.py → ResponseValidatorABC.py} +26 -40
- edsl/questions/SimpleAskMixin.py +3 -4
- edsl/questions/__init__.py +1 -2
- edsl/questions/derived/QuestionLinearScale.py +3 -6
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +3 -17
- edsl/questions/question_registry.py +1 -1
- edsl/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +7 -170
- edsl/results/DatasetExportMixin.py +305 -168
- edsl/results/DatasetTree.py +8 -28
- edsl/results/Result.py +206 -298
- edsl/results/Results.py +131 -149
- edsl/results/ResultsDBMixin.py +238 -0
- edsl/results/ResultsExportMixin.py +0 -2
- edsl/results/{results_selector.py → Selector.py} +13 -23
- edsl/results/TableDisplay.py +171 -98
- edsl/results/__init__.py +1 -1
- edsl/scenarios/FileStore.py +239 -150
- edsl/scenarios/Scenario.py +193 -90
- edsl/scenarios/ScenarioHtmlMixin.py +3 -4
- edsl/scenarios/{scenario_join.py → ScenarioJoin.py} +6 -10
- edsl/scenarios/ScenarioList.py +244 -415
- edsl/scenarios/ScenarioListExportMixin.py +7 -0
- edsl/scenarios/ScenarioListPdfMixin.py +37 -15
- edsl/scenarios/__init__.py +2 -1
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +12 -5
- edsl/surveys/Rule.py +4 -5
- edsl/surveys/RuleCollection.py +27 -25
- edsl/surveys/Survey.py +791 -270
- edsl/surveys/SurveyCSS.py +8 -20
- edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +9 -11
- edsl/surveys/__init__.py +2 -4
- edsl/surveys/descriptors.py +2 -6
- edsl/surveys/instructions/ChangeInstruction.py +2 -1
- edsl/surveys/instructions/Instruction.py +13 -4
- edsl/surveys/instructions/InstructionCollection.py +6 -11
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/utilities.py +23 -35
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/METADATA +10 -12
- edsl-0.1.39.dev1.dist-info/RECORD +277 -0
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/WHEEL +1 -1
- edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
- edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
- edsl/agents/question_option_processor.py +0 -172
- edsl/coop/CoopFunctionsMixin.py +0 -15
- edsl/coop/ExpectedParrotKeyHandler.py +0 -125
- edsl/exceptions/inference_services.py +0 -5
- edsl/inference_services/AvailableModelCacheHandler.py +0 -184
- edsl/inference_services/AvailableModelFetcher.py +0 -215
- edsl/inference_services/ServiceAvailability.py +0 -135
- edsl/inference_services/data_structures.py +0 -134
- edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -223
- edsl/jobs/FetchInvigilator.py +0 -47
- edsl/jobs/InterviewTaskManager.py +0 -98
- edsl/jobs/InterviewsConstructor.py +0 -50
- edsl/jobs/JobsComponentConstructor.py +0 -189
- edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
- edsl/jobs/RequestTokenEstimator.py +0 -30
- edsl/jobs/async_interview_runner.py +0 -138
- edsl/jobs/buckets/TokenBucketAPI.py +0 -211
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/check_survey_scenario_compatibility.py +0 -85
- edsl/jobs/data_structures.py +0 -120
- edsl/jobs/decorators.py +0 -35
- edsl/jobs/jobs_status_enums.py +0 -9
- edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
- edsl/jobs/results_exceptions_handler.py +0 -98
- edsl/language_models/ComputeCost.py +0 -63
- edsl/language_models/PriceManager.py +0 -127
- edsl/language_models/RawResponseHandler.py +0 -106
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +0 -131
- edsl/language_models/model.py +0 -256
- edsl/notebooks/NotebookToLaTeX.py +0 -142
- edsl/questions/ExceptionExplainer.py +0 -77
- edsl/questions/HTMLQuestion.py +0 -103
- edsl/questions/QuestionMatrix.py +0 -265
- edsl/questions/data_structures.py +0 -20
- edsl/questions/loop_processor.py +0 -149
- edsl/questions/response_validator_factory.py +0 -34
- edsl/questions/templates/matrix/__init__.py +0 -1
- edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
- edsl/questions/templates/matrix/question_presentation.jinja +0 -20
- edsl/results/MarkdownToDocx.py +0 -122
- edsl/results/MarkdownToPDF.py +0 -111
- edsl/results/TextEditor.py +0 -50
- edsl/results/file_exports.py +0 -252
- edsl/results/smart_objects.py +0 -96
- edsl/results/table_data_class.py +0 -12
- edsl/results/table_renderers.py +0 -118
- edsl/scenarios/ConstructDownloadLink.py +0 -109
- edsl/scenarios/DocumentChunker.py +0 -102
- edsl/scenarios/DocxScenario.py +0 -16
- edsl/scenarios/PdfExtractor.py +0 -40
- edsl/scenarios/directory_scanner.py +0 -96
- edsl/scenarios/file_methods.py +0 -85
- edsl/scenarios/handlers/__init__.py +0 -13
- edsl/scenarios/handlers/csv.py +0 -49
- edsl/scenarios/handlers/docx.py +0 -76
- edsl/scenarios/handlers/html.py +0 -37
- edsl/scenarios/handlers/json.py +0 -111
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/scenarios/handlers/md.py +0 -51
- edsl/scenarios/handlers/pdf.py +0 -68
- edsl/scenarios/handlers/png.py +0 -39
- edsl/scenarios/handlers/pptx.py +0 -105
- edsl/scenarios/handlers/py.py +0 -294
- edsl/scenarios/handlers/sql.py +0 -313
- edsl/scenarios/handlers/sqlite.py +0 -149
- edsl/scenarios/handlers/txt.py +0 -33
- edsl/scenarios/scenario_selector.py +0 -156
- edsl/surveys/ConstructDAG.py +0 -92
- edsl/surveys/EditSurvey.py +0 -221
- edsl/surveys/InstructionHandler.py +0 -100
- edsl/surveys/MemoryManagement.py +0 -72
- edsl/surveys/RuleManager.py +0 -172
- edsl/surveys/Simulator.py +0 -75
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/utilities/PrettyList.py +0 -56
- edsl/utilities/is_notebook.py +0 -18
- edsl/utilities/is_valid_variable_name.py +0 -11
- edsl/utilities/remove_edsl_version.py +0 -24
- edsl-0.1.39.dist-info/RECORD +0 -358
- /edsl/questions/{register_questions_meta.py → RegisterQuestionsMeta.py} +0 -0
- /edsl/results/{results_fetch_mixin.py → ResultsFetchMixin.py} +0 -0
- /edsl/results/{results_tools_mixin.py → ResultsToolsMixin.py} +0 -0
- {edsl-0.1.39.dist-info → edsl-0.1.39.dev1.dist-info}/LICENSE +0 -0
edsl/agents/Agent.py
CHANGED
@@ -4,46 +4,20 @@ from __future__ import annotations
|
|
4
4
|
import copy
|
5
5
|
import inspect
|
6
6
|
import types
|
7
|
-
from typing import
|
8
|
-
Callable,
|
9
|
-
Optional,
|
10
|
-
Union,
|
11
|
-
Any,
|
12
|
-
TYPE_CHECKING,
|
13
|
-
Protocol,
|
14
|
-
runtime_checkable,
|
15
|
-
TypeVar,
|
16
|
-
)
|
17
|
-
from contextlib import contextmanager
|
18
|
-
from dataclasses import dataclass
|
19
|
-
|
20
|
-
# Type variable for the Agent class
|
21
|
-
A = TypeVar("A", bound="Agent")
|
7
|
+
from typing import Callable, Optional, Union, Any, TYPE_CHECKING
|
22
8
|
|
23
9
|
if TYPE_CHECKING:
|
24
|
-
from edsl
|
25
|
-
from edsl.surveys.Survey import Survey
|
26
|
-
from edsl.scenarios.Scenario import Scenario
|
10
|
+
from edsl import Cache, Survey, Scenario
|
27
11
|
from edsl.language_models import LanguageModel
|
28
12
|
from edsl.surveys.MemoryPlan import MemoryPlan
|
29
13
|
from edsl.questions import QuestionBase
|
30
14
|
from edsl.agents.Invigilator import InvigilatorBase
|
31
|
-
from edsl.prompts import Prompt
|
32
|
-
from edsl.questions.QuestionBase import QuestionBase
|
33
|
-
from edsl.scenarios.Scenario import Scenario
|
34
|
-
|
35
|
-
|
36
|
-
@runtime_checkable
|
37
|
-
class DirectAnswerMethod(Protocol):
|
38
|
-
"""Protocol defining the required signature for direct answer methods."""
|
39
|
-
|
40
|
-
def __call__(self, self_: A, question: QuestionBase, scenario: Scenario) -> Any: ...
|
41
|
-
|
42
15
|
|
43
16
|
from uuid import uuid4
|
44
17
|
|
45
18
|
from edsl.Base import Base
|
46
|
-
from edsl.
|
19
|
+
from edsl.prompts import Prompt
|
20
|
+
from edsl.exceptions import QuestionScenarioRenderError
|
47
21
|
|
48
22
|
from edsl.exceptions.agents import (
|
49
23
|
AgentErrors,
|
@@ -60,25 +34,17 @@ from edsl.agents.descriptors import (
|
|
60
34
|
)
|
61
35
|
from edsl.utilities.decorators import (
|
62
36
|
sync_wrapper,
|
37
|
+
add_edsl_version,
|
38
|
+
remove_edsl_version,
|
63
39
|
)
|
64
|
-
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
65
40
|
from edsl.data_transfer_models import AgentResponseDict
|
66
41
|
from edsl.utilities.restricted_python import create_restricted_function
|
67
42
|
|
68
|
-
from edsl.scenarios.Scenario import Scenario
|
69
|
-
|
70
|
-
|
71
|
-
class AgentTraits(Scenario):
|
72
|
-
"""A class representing the traits of an agent."""
|
73
|
-
|
74
|
-
def __repr__(self):
|
75
|
-
return f"{self.data}"
|
76
|
-
|
77
43
|
|
78
44
|
class Agent(Base):
|
79
45
|
"""An class representing an agent that can answer questions."""
|
80
46
|
|
81
|
-
|
47
|
+
__doc__ = "https://docs.expectedparrot.com/en/latest/agents.html"
|
82
48
|
|
83
49
|
default_instruction = """You are answering questions as if you were a human. Do not break character."""
|
84
50
|
|
@@ -111,8 +77,6 @@ class Agent(Base):
|
|
111
77
|
:param instruction: Instructions for the agent in how to answer questions.
|
112
78
|
:param trait_presentation_template: A template for how to present the agent's traits.
|
113
79
|
:param dynamic_traits_function: A function that returns a dictionary of traits.
|
114
|
-
:param dynamic_traits_function_source_code: The source code for the dynamic traits function.
|
115
|
-
:param dynamic_traits_function_name: The name of the dynamic traits function.
|
116
80
|
|
117
81
|
The `traits` parameter is a dictionary of traits that the agent has.
|
118
82
|
These traits are used to construct a prompt that is presented to the LLM.
|
@@ -155,59 +119,17 @@ class Agent(Base):
|
|
155
119
|
See see how these are used to actually construct the prompt that is presented to the LLM, see :py:class:`edsl.agents.Invigilator.InvigilatorBase`.
|
156
120
|
|
157
121
|
"""
|
158
|
-
self._initialize_basic_attributes(traits, name, codebook)
|
159
|
-
self._initialize_instruction(instruction)
|
160
|
-
self._initialize_dynamic_traits_function(
|
161
|
-
dynamic_traits_function,
|
162
|
-
dynamic_traits_function_source_code,
|
163
|
-
dynamic_traits_function_name,
|
164
|
-
)
|
165
|
-
self._initialize_answer_question_directly(
|
166
|
-
answer_question_directly_source_code, answer_question_directly_function_name
|
167
|
-
)
|
168
|
-
self._check_dynamic_traits_function()
|
169
|
-
self._initialize_traits_presentation_template(traits_presentation_template)
|
170
|
-
self.current_question = None
|
171
|
-
|
172
|
-
def _initialize_basic_attributes(self, traits, name, codebook) -> None:
|
173
|
-
"""Initialize the basic attributes of the agent."""
|
174
122
|
self.name = name
|
175
|
-
self._traits =
|
123
|
+
self._traits = traits or dict()
|
176
124
|
self.codebook = codebook or dict()
|
177
|
-
|
178
|
-
def _initialize_instruction(self, instruction) -> None:
|
179
|
-
"""Initialize the instruction for the agent i.e., how the agent should answer questions."""
|
180
125
|
if instruction is None:
|
181
126
|
self.instruction = self.default_instruction
|
182
|
-
self._instruction = self.default_instruction
|
183
|
-
self.set_instructions = False
|
184
127
|
else:
|
185
128
|
self.instruction = instruction
|
186
|
-
|
187
|
-
self.set_instructions = True
|
188
|
-
|
189
|
-
def _initialize_traits_presentation_template(
|
190
|
-
self, traits_presentation_template
|
191
|
-
) -> None:
|
192
|
-
"""Initialize the traits presentation template. How the agent's traits are presented to the LLM."""
|
193
|
-
if traits_presentation_template is not None:
|
194
|
-
self._traits_presentation_template = traits_presentation_template
|
195
|
-
self.traits_presentation_template = traits_presentation_template
|
196
|
-
self.set_traits_presentation_template = True
|
197
|
-
else:
|
198
|
-
self.traits_presentation_template = "Your traits: {{traits}}"
|
199
|
-
self.set_traits_presentation_template = False
|
200
|
-
|
201
|
-
def _initialize_dynamic_traits_function(
|
202
|
-
self,
|
203
|
-
dynamic_traits_function,
|
204
|
-
dynamic_traits_function_source_code,
|
205
|
-
dynamic_traits_function_name,
|
206
|
-
) -> None:
|
207
|
-
"""Initialize the dynamic traits function i.e., a function that returns a dictionary of traits based on the question."""
|
208
|
-
# Deal with dynamic traits function
|
129
|
+
# self.instruction = instruction or self.default_instruction
|
209
130
|
self.dynamic_traits_function = dynamic_traits_function
|
210
131
|
|
132
|
+
# Deal with dynamic traits function
|
211
133
|
if self.dynamic_traits_function:
|
212
134
|
self.dynamic_traits_function_name = self.dynamic_traits_function.__name__
|
213
135
|
self.has_dynamic_traits_function = True
|
@@ -220,11 +142,7 @@ class Agent(Base):
|
|
220
142
|
dynamic_traits_function_name, dynamic_traits_function
|
221
143
|
)
|
222
144
|
|
223
|
-
|
224
|
-
self,
|
225
|
-
answer_question_directly_source_code,
|
226
|
-
answer_question_directly_function_name,
|
227
|
-
) -> None:
|
145
|
+
# Deal with direct answer function
|
228
146
|
if answer_question_directly_source_code:
|
229
147
|
self.answer_question_directly_function_name = (
|
230
148
|
answer_question_directly_function_name
|
@@ -236,56 +154,18 @@ class Agent(Base):
|
|
236
154
|
bound_method = types.MethodType(protected_method, self)
|
237
155
|
setattr(self, "answer_question_directly", bound_method)
|
238
156
|
|
239
|
-
|
240
|
-
|
241
|
-
|
157
|
+
self._check_dynamic_traits_function()
|
158
|
+
|
159
|
+
self.current_question = None
|
160
|
+
|
242
161
|
if traits_presentation_template is not None:
|
243
162
|
self._traits_presentation_template = traits_presentation_template
|
244
163
|
self.traits_presentation_template = traits_presentation_template
|
245
|
-
self.set_traits_presentation_template = True
|
246
164
|
else:
|
247
165
|
self.traits_presentation_template = "Your traits: {{traits}}"
|
248
|
-
self.set_traits_presentation_template = False
|
249
|
-
|
250
|
-
def duplicate(self) -> Agent:
|
251
|
-
"""Return a duplicate of the agent.
|
252
|
-
|
253
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5}, codebook = {'age': 'Their age is'})
|
254
|
-
>>> a2 = a.duplicate()
|
255
|
-
>>> a2 == a
|
256
|
-
True
|
257
|
-
>>> id(a) == id(a2)
|
258
|
-
False
|
259
|
-
>>> def f(self, question, scenario): return "I am a direct answer."
|
260
|
-
>>> a.add_direct_question_answering_method(f)
|
261
|
-
>>> hasattr(a, "answer_question_directly")
|
262
|
-
True
|
263
|
-
>>> a2 = a.duplicate()
|
264
|
-
>>> a2.answer_question_directly(None, None)
|
265
|
-
'I am a direct answer.'
|
266
|
-
|
267
|
-
>>> a = Agent(traits = {'age': 10}, instruction = "Have fun!")
|
268
|
-
>>> a2 = a.duplicate()
|
269
|
-
>>> a2.instruction
|
270
|
-
'Have fun!'
|
271
|
-
"""
|
272
|
-
new_agent = Agent.from_dict(self.to_dict())
|
273
|
-
if hasattr(self, "answer_question_directly"):
|
274
|
-
answer_question_directly = self.answer_question_directly
|
275
|
-
newf = lambda self, question, scenario: answer_question_directly(
|
276
|
-
question, scenario
|
277
|
-
)
|
278
|
-
new_agent.add_direct_question_answering_method(newf)
|
279
|
-
if hasattr(self, "dynamic_traits_function"):
|
280
|
-
dynamic_traits_function = self.dynamic_traits_function
|
281
|
-
new_agent.dynamic_traits_function = dynamic_traits_function
|
282
|
-
return new_agent
|
283
166
|
|
284
167
|
@property
|
285
168
|
def agent_persona(self) -> Prompt:
|
286
|
-
"""Return the agent persona template."""
|
287
|
-
from edsl.prompts.Prompt import Prompt
|
288
|
-
|
289
169
|
return Prompt(text=self.traits_presentation_template)
|
290
170
|
|
291
171
|
def prompt(self) -> str:
|
@@ -361,111 +241,59 @@ class Agent(Base):
|
|
361
241
|
else:
|
362
242
|
return self.dynamic_traits_function()
|
363
243
|
else:
|
364
|
-
return
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
self.
|
369
|
-
|
370
|
-
|
371
|
-
finally:
|
372
|
-
self._traits = AgentTraits(self._traits)
|
373
|
-
|
374
|
-
def _check_before_modifying_traits(self):
|
375
|
-
"""Check before modifying traits."""
|
376
|
-
if self.has_dynamic_traits_function:
|
377
|
-
raise AgentErrors(
|
378
|
-
"You cannot modify the traits of an agent that has a dynamic traits function.",
|
379
|
-
"If you want to modify the traits, you should remove the dynamic traits function.",
|
380
|
-
)
|
244
|
+
return self._traits
|
245
|
+
|
246
|
+
def _repr_html_(self):
|
247
|
+
# d = self.to_dict(add_edsl_version=False)
|
248
|
+
d = self.traits
|
249
|
+
data = [[k, v] for k, v in d.items()]
|
250
|
+
from tabulate import tabulate
|
381
251
|
|
382
|
-
|
383
|
-
|
384
|
-
with self.modify_traits_context():
|
385
|
-
self._traits = traits
|
386
|
-
# self._check_before_modifying_traits()
|
387
|
-
# self._traits = AgentTraits(traits)
|
252
|
+
table = str(tabulate(data, headers=["keys", "values"], tablefmt="html"))
|
253
|
+
return f"<pre>{table}</pre>"
|
388
254
|
|
389
255
|
def rename(
|
390
|
-
self,
|
391
|
-
old_name_or_dict: Union[str, dict[str, str]],
|
392
|
-
new_name: Optional[str] = None,
|
256
|
+
self, old_name_or_dict: Union[str, dict], new_name: Optional[str] = None
|
393
257
|
) -> Agent:
|
394
258
|
"""Rename a trait.
|
395
259
|
|
396
|
-
:param old_name_or_dict: The old name of the trait or a dictionary of old names and new names.
|
397
|
-
:param new_name: The new name of the trait.
|
398
|
-
|
399
260
|
Example usage:
|
400
261
|
|
401
262
|
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
402
|
-
>>>
|
403
|
-
>>> newa == Agent(traits = {'years': 10, 'hair': 'brown', 'height': 5.5})
|
263
|
+
>>> a.rename("age", "years") == Agent(traits = {'years': 10, 'hair': 'brown', 'height': 5.5})
|
404
264
|
True
|
405
265
|
|
406
|
-
>>>
|
407
|
-
|
266
|
+
>>> a.rename({'years': 'smage'})
|
267
|
+
Agent(traits = {'hair': 'brown', 'height': 5.5, 'smage': 10})
|
408
268
|
|
409
269
|
"""
|
410
|
-
|
270
|
+
if isinstance(old_name_or_dict, dict) and new_name is None:
|
271
|
+
for old_name, new_name in old_name_or_dict.items():
|
272
|
+
self = self._rename(old_name, new_name)
|
273
|
+
return self
|
274
|
+
|
411
275
|
if isinstance(old_name_or_dict, dict) and new_name:
|
412
276
|
raise AgentErrors(
|
413
277
|
f"You passed a dict: {old_name_or_dict} and a new name: {new_name}. You should pass only a dict."
|
414
278
|
)
|
415
279
|
|
416
|
-
if isinstance(old_name_or_dict, dict) and new_name is None:
|
417
|
-
return self._rename_dict(old_name_or_dict)
|
418
|
-
|
419
280
|
if isinstance(old_name_or_dict, str):
|
420
|
-
|
281
|
+
self._rename(old_name_or_dict, new_name)
|
282
|
+
return self
|
421
283
|
|
422
284
|
raise AgentErrors("Something is not right with Agent renaming")
|
423
285
|
|
424
|
-
def _rename_dict(self, renaming_dict: dict[str, str]) -> Agent:
|
425
|
-
"""
|
426
|
-
Internal method to rename traits using a dictionary.
|
427
|
-
The keys should all be old names and the values should all be new names.
|
428
|
-
|
429
|
-
Example usage:
|
430
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
431
|
-
>>> a._rename_dict({"age": "years", "height": "feet"})
|
432
|
-
Agent(traits = {'years': 10, 'hair': 'brown', 'feet': 5.5})
|
433
|
-
|
434
|
-
"""
|
435
|
-
try:
|
436
|
-
assert all(k in self.traits for k in renaming_dict.keys())
|
437
|
-
except AssertionError:
|
438
|
-
raise AgentErrors(
|
439
|
-
f"The trait(s) {set(renaming_dict.keys()) - set(self.traits.keys())} do not exist in the agent's traits, which are {self.traits}."
|
440
|
-
)
|
441
|
-
new_agent = self.duplicate()
|
442
|
-
new_agent.traits = {renaming_dict.get(k, k): v for k, v in self.traits.items()}
|
443
|
-
return new_agent
|
444
|
-
|
445
286
|
def _rename(self, old_name: str, new_name: str) -> Agent:
|
446
287
|
"""Rename a trait.
|
447
288
|
|
448
289
|
Example usage:
|
449
290
|
|
450
291
|
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
451
|
-
>>> a.
|
452
|
-
|
453
|
-
|
292
|
+
>>> a.rename("age", "years") == Agent(traits = {'years': 10, 'hair': 'brown', 'height': 5.5})
|
293
|
+
True
|
454
294
|
"""
|
455
|
-
|
456
|
-
|
457
|
-
except AssertionError:
|
458
|
-
raise AgentErrors(
|
459
|
-
f"The trait '{old_name}' does not exist in the agent's traits, which are {self.traits}."
|
460
|
-
)
|
461
|
-
newagent = self.duplicate()
|
462
|
-
newagent.traits = {
|
463
|
-
new_name if k == old_name else k: v for k, v in self.traits.items()
|
464
|
-
}
|
465
|
-
newagent.codebook = {
|
466
|
-
new_name if k == old_name else k: v for k, v in self.codebook.items()
|
467
|
-
}
|
468
|
-
return newagent
|
295
|
+
self.traits[new_name] = self.traits.pop(old_name)
|
296
|
+
return self
|
469
297
|
|
470
298
|
def __getitem__(self, key):
|
471
299
|
"""Allow for accessing traits using the bracket notation.
|
@@ -496,7 +324,7 @@ class Agent(Base):
|
|
496
324
|
|
497
325
|
def add_direct_question_answering_method(
|
498
326
|
self,
|
499
|
-
method:
|
327
|
+
method: Callable,
|
500
328
|
validate_response: bool = False,
|
501
329
|
translate_response: bool = False,
|
502
330
|
) -> None:
|
@@ -525,12 +353,6 @@ class Agent(Base):
|
|
525
353
|
self.validate_response = validate_response
|
526
354
|
self.translate_response = translate_response
|
527
355
|
|
528
|
-
# if not isinstance(method, DirectAnswerMethod):
|
529
|
-
# raise AgentDirectAnswerFunctionError(
|
530
|
-
# f"Method {method} does not match required signature. "
|
531
|
-
# "Must take (self, question, scenario) parameters."
|
532
|
-
# )
|
533
|
-
|
534
356
|
signature = inspect.signature(method)
|
535
357
|
for argument in ["question", "scenario", "self"]:
|
536
358
|
if argument not in signature.parameters:
|
@@ -549,11 +371,12 @@ class Agent(Base):
|
|
549
371
|
survey: Optional["Survey"] = None,
|
550
372
|
scenario: Optional["Scenario"] = None,
|
551
373
|
model: Optional["LanguageModel"] = None,
|
374
|
+
debug: bool = False,
|
552
375
|
memory_plan: Optional["MemoryPlan"] = None,
|
553
376
|
current_answers: Optional[dict] = None,
|
554
377
|
iteration: int = 1,
|
378
|
+
sidecar_model=None,
|
555
379
|
raise_validation_errors: bool = True,
|
556
|
-
key_lookup: Optional["KeyLookup"] = None,
|
557
380
|
) -> "InvigilatorBase":
|
558
381
|
"""Create an Invigilator.
|
559
382
|
|
@@ -568,9 +391,7 @@ class Agent(Base):
|
|
568
391
|
An invigator is an object that is responsible for administering a question to an agent and
|
569
392
|
recording the responses.
|
570
393
|
"""
|
571
|
-
from edsl
|
572
|
-
|
573
|
-
from edsl.scenarios.Scenario import Scenario
|
394
|
+
from edsl import Model, Scenario
|
574
395
|
|
575
396
|
cache = cache
|
576
397
|
self.current_question = question
|
@@ -581,12 +402,13 @@ class Agent(Base):
|
|
581
402
|
scenario=scenario,
|
582
403
|
survey=survey,
|
583
404
|
model=model,
|
405
|
+
debug=debug,
|
584
406
|
memory_plan=memory_plan,
|
585
407
|
current_answers=current_answers,
|
586
408
|
iteration=iteration,
|
587
409
|
cache=cache,
|
410
|
+
sidecar_model=sidecar_model,
|
588
411
|
raise_validation_errors=raise_validation_errors,
|
589
|
-
key_lookup=key_lookup,
|
590
412
|
)
|
591
413
|
if hasattr(self, "validate_response"):
|
592
414
|
invigilator.validate_response = self.validate_response
|
@@ -606,7 +428,6 @@ class Agent(Base):
|
|
606
428
|
memory_plan: Optional[MemoryPlan] = None,
|
607
429
|
current_answers: Optional[dict] = None,
|
608
430
|
iteration: int = 0,
|
609
|
-
key_lookup: Optional["KeyLookup"] = None,
|
610
431
|
) -> AgentResponseDict:
|
611
432
|
"""
|
612
433
|
Answer a posed question.
|
@@ -621,7 +442,7 @@ class Agent(Base):
|
|
621
442
|
|
622
443
|
>>> a = Agent(traits = {})
|
623
444
|
>>> a.add_direct_question_answering_method(lambda self, question, scenario: "I am a direct answer.")
|
624
|
-
>>> from edsl
|
445
|
+
>>> from edsl import QuestionFreeText
|
625
446
|
>>> q = QuestionFreeText.example()
|
626
447
|
>>> a.answer_question(question = q, cache = False).answer
|
627
448
|
'I am a direct answer.'
|
@@ -636,35 +457,16 @@ class Agent(Base):
|
|
636
457
|
scenario=scenario,
|
637
458
|
survey=survey,
|
638
459
|
model=model,
|
460
|
+
debug=debug,
|
639
461
|
memory_plan=memory_plan,
|
640
462
|
current_answers=current_answers,
|
641
463
|
iteration=iteration,
|
642
|
-
key_lookup=key_lookup,
|
643
464
|
)
|
644
465
|
response: AgentResponseDict = await invigilator.async_answer_question()
|
645
466
|
return response
|
646
467
|
|
647
468
|
answer_question = sync_wrapper(async_answer_question)
|
648
469
|
|
649
|
-
def _get_invigilator_class(self, question: QuestionBase) -> Type[InvigilatorBase]:
|
650
|
-
"""Get the invigilator class for a question.
|
651
|
-
|
652
|
-
This method returns the invigilator class that should be used to answer a question.
|
653
|
-
The invigilator class is determined by the type of question and the type of agent.
|
654
|
-
"""
|
655
|
-
from edsl.agents.Invigilator import (
|
656
|
-
InvigilatorHuman,
|
657
|
-
InvigilatorFunctional,
|
658
|
-
InvigilatorAI,
|
659
|
-
)
|
660
|
-
|
661
|
-
if hasattr(question, "answer_question_directly"):
|
662
|
-
return InvigilatorFunctional
|
663
|
-
elif hasattr(self, "answer_question_directly"):
|
664
|
-
return InvigilatorHuman
|
665
|
-
else:
|
666
|
-
return InvigilatorAI
|
667
|
-
|
668
470
|
def _create_invigilator(
|
669
471
|
self,
|
670
472
|
question: QuestionBase,
|
@@ -672,25 +474,53 @@ class Agent(Base):
|
|
672
474
|
scenario: Optional[Scenario] = None,
|
673
475
|
model: Optional[LanguageModel] = None,
|
674
476
|
survey: Optional[Survey] = None,
|
477
|
+
debug: bool = False,
|
675
478
|
memory_plan: Optional[MemoryPlan] = None,
|
676
479
|
current_answers: Optional[dict] = None,
|
677
480
|
iteration: int = 0,
|
481
|
+
sidecar_model=None,
|
678
482
|
raise_validation_errors: bool = True,
|
679
|
-
key_lookup: Optional["KeyLookup"] = None,
|
680
483
|
) -> "InvigilatorBase":
|
681
484
|
"""Create an Invigilator."""
|
682
|
-
from edsl
|
683
|
-
from edsl
|
485
|
+
from edsl import Model
|
486
|
+
from edsl import Scenario
|
684
487
|
|
685
488
|
model = model or Model()
|
686
489
|
scenario = scenario or Scenario()
|
687
490
|
|
491
|
+
from edsl.agents.Invigilator import (
|
492
|
+
InvigilatorHuman,
|
493
|
+
InvigilatorFunctional,
|
494
|
+
InvigilatorAI,
|
495
|
+
InvigilatorBase,
|
496
|
+
)
|
497
|
+
|
688
498
|
if cache is None:
|
689
499
|
from edsl.data.Cache import Cache
|
690
500
|
|
691
501
|
cache = Cache()
|
692
502
|
|
693
|
-
|
503
|
+
if debug:
|
504
|
+
raise NotImplementedError("Debug mode is not yet implemented.")
|
505
|
+
# use the question's _simulate_answer method
|
506
|
+
# invigilator_class = InvigilatorDebug
|
507
|
+
elif hasattr(question, "answer_question_directly"):
|
508
|
+
# It's a functional question and the answer only depends on the agent's traits & the scenario
|
509
|
+
invigilator_class = InvigilatorFunctional
|
510
|
+
elif hasattr(self, "answer_question_directly"):
|
511
|
+
# this of the case where the agent has a method that can answer the question directly
|
512
|
+
# this occurrs when 'answer_question_directly' has been given to the
|
513
|
+
# which happens when the agent is created from an existing survey
|
514
|
+
invigilator_class = InvigilatorHuman
|
515
|
+
else:
|
516
|
+
# this means an LLM agent will be used. This is the standard case.
|
517
|
+
invigilator_class = InvigilatorAI
|
518
|
+
|
519
|
+
if sidecar_model is not None:
|
520
|
+
# this is the case when a 'simple' model is being used
|
521
|
+
from edsl.agents.Invigilator import InvigilatorSidecar
|
522
|
+
|
523
|
+
invigilator_class = InvigilatorSidecar
|
694
524
|
|
695
525
|
invigilator = invigilator_class(
|
696
526
|
self,
|
@@ -702,24 +532,22 @@ class Agent(Base):
|
|
702
532
|
current_answers=current_answers,
|
703
533
|
iteration=iteration,
|
704
534
|
cache=cache,
|
535
|
+
sidecar_model=sidecar_model,
|
705
536
|
raise_validation_errors=raise_validation_errors,
|
706
|
-
key_lookup=key_lookup,
|
707
537
|
)
|
708
538
|
return invigilator
|
709
539
|
|
710
540
|
def select(self, *traits: str) -> Agent:
|
711
541
|
"""Selects agents with only the references traits
|
712
542
|
|
713
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5}
|
714
|
-
>>> a
|
715
|
-
Agent(traits = {'age': 10, 'hair': 'brown', 'height': 5.5}, codebook = {'age': 'Their age is'})
|
543
|
+
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
716
544
|
|
717
545
|
|
718
546
|
>>> a.select("age", "height")
|
719
|
-
Agent(traits = {'age': 10, 'height': 5.5}
|
547
|
+
Agent(traits = {'age': 10, 'height': 5.5})
|
720
548
|
|
721
|
-
>>> a.select("
|
722
|
-
Agent(traits = {'
|
549
|
+
>>> a.select("age")
|
550
|
+
Agent(traits = {'age': 10})
|
723
551
|
|
724
552
|
"""
|
725
553
|
|
@@ -728,17 +556,7 @@ class Agent(Base):
|
|
728
556
|
else:
|
729
557
|
traits_to_select = list(traits)
|
730
558
|
|
731
|
-
|
732
|
-
return {k: v for k, v in d.items() if v is not None}
|
733
|
-
|
734
|
-
newagent = self.duplicate()
|
735
|
-
newagent.traits = {
|
736
|
-
trait: self.traits.get(trait, None) for trait in traits_to_select
|
737
|
-
}
|
738
|
-
newagent.codebook = _remove_none(
|
739
|
-
{trait: self.codebook.get(trait, None) for trait in traits_to_select}
|
740
|
-
)
|
741
|
-
return newagent
|
559
|
+
return Agent(traits={trait: self.traits[trait] for trait in traits_to_select})
|
742
560
|
|
743
561
|
def __add__(self, other_agent: Optional[Agent] = None) -> Agent:
|
744
562
|
"""
|
@@ -757,10 +575,6 @@ class Agent(Base):
|
|
757
575
|
...
|
758
576
|
edsl.exceptions.agents.AgentCombinationError: The agents have overlapping traits: {'age'}.
|
759
577
|
...
|
760
|
-
>>> a1 = Agent(traits = {"age": 10}, codebook = {"age": "Their age is"})
|
761
|
-
>>> a2 = Agent(traits = {"height": 5.5}, codebook = {"height": "Their height is"})
|
762
|
-
>>> a1 + a2
|
763
|
-
Agent(traits = {'age': 10, 'height': 5.5}, codebook = {'age': 'Their age is', 'height': 'Their height is'})
|
764
578
|
"""
|
765
579
|
if other_agent is None:
|
766
580
|
return self
|
@@ -769,14 +583,9 @@ class Agent(Base):
|
|
769
583
|
f"The agents have overlapping traits: {common_traits}."
|
770
584
|
)
|
771
585
|
else:
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
d = self.traits | other_agent.traits
|
776
|
-
newagent = self.duplicate()
|
777
|
-
newagent.traits = d
|
778
|
-
newagent.codebook = new_codebook
|
779
|
-
return newagent
|
586
|
+
new_agent = Agent(traits=copy.deepcopy(self.traits))
|
587
|
+
new_agent.traits.update(other_agent.traits)
|
588
|
+
return new_agent
|
780
589
|
|
781
590
|
def __eq__(self, other: Agent) -> bool:
|
782
591
|
"""Check if two agents are equal.
|
@@ -793,11 +602,7 @@ class Agent(Base):
|
|
793
602
|
return self.data == other.data
|
794
603
|
|
795
604
|
def __getattr__(self, name):
|
796
|
-
|
797
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
798
|
-
>>> a.age
|
799
|
-
10
|
800
|
-
"""
|
605
|
+
# This will be called only if 'name' is not found in the usual places
|
801
606
|
if name == "has_dynamic_traits_function":
|
802
607
|
return self.has_dynamic_traits_function
|
803
608
|
|
@@ -819,6 +624,12 @@ class Agent(Base):
|
|
819
624
|
if "_traits" not in self.__dict__:
|
820
625
|
self._traits = {}
|
821
626
|
|
627
|
+
def print(self) -> None:
|
628
|
+
from rich import print_json
|
629
|
+
import json
|
630
|
+
|
631
|
+
print_json(json.dumps(self.to_dict()))
|
632
|
+
|
822
633
|
def __repr__(self) -> str:
|
823
634
|
"""Return representation of Agent."""
|
824
635
|
class_name = self.__class__.__name__
|
@@ -829,6 +640,14 @@ class Agent(Base):
|
|
829
640
|
]
|
830
641
|
return f"{class_name}({', '.join(items)})"
|
831
642
|
|
643
|
+
# def _repr_html_(self):
|
644
|
+
# from edsl.utilities.utilities import data_to_html
|
645
|
+
|
646
|
+
# return data_to_html(self.to_dict())
|
647
|
+
|
648
|
+
#######################
|
649
|
+
# SERIALIZATION METHODS
|
650
|
+
#######################
|
832
651
|
@property
|
833
652
|
def data(self) -> dict:
|
834
653
|
"""Format the data for serialization.
|
@@ -859,9 +678,9 @@ class Agent(Base):
|
|
859
678
|
if dynamic_traits_func:
|
860
679
|
func = inspect.getsource(dynamic_traits_func)
|
861
680
|
raw_data["dynamic_traits_function_source_code"] = func
|
862
|
-
raw_data[
|
863
|
-
|
864
|
-
|
681
|
+
raw_data[
|
682
|
+
"dynamic_traits_function_name"
|
683
|
+
] = self.dynamic_traits_function_name
|
865
684
|
if hasattr(self, "answer_question_directly"):
|
866
685
|
raw_data.pop(
|
867
686
|
"answer_question_directly", None
|
@@ -875,23 +694,18 @@ class Agent(Base):
|
|
875
694
|
raw_data["answer_question_directly_source_code"] = inspect.getsource(
|
876
695
|
answer_question_directly_func
|
877
696
|
)
|
878
|
-
raw_data[
|
879
|
-
|
880
|
-
|
881
|
-
raw_data["traits"] = dict(raw_data["traits"])
|
697
|
+
raw_data[
|
698
|
+
"answer_question_directly_function_name"
|
699
|
+
] = self.answer_question_directly_function_name
|
882
700
|
|
883
701
|
return raw_data
|
884
702
|
|
885
703
|
def __hash__(self) -> int:
|
886
|
-
"""Return a hash of the agent.
|
887
|
-
|
888
|
-
>>> hash(Agent.example())
|
889
|
-
2067581884874391607
|
890
|
-
"""
|
891
704
|
from edsl.utilities.utilities import dict_hash
|
892
705
|
|
893
706
|
return dict_hash(self.to_dict(add_edsl_version=False))
|
894
707
|
|
708
|
+
# @add_edsl_version
|
895
709
|
def to_dict(self, add_edsl_version=True) -> dict[str, Union[dict, bool]]:
|
896
710
|
"""Serialize to a dictionary with EDSL info.
|
897
711
|
|
@@ -899,22 +713,9 @@ class Agent(Base):
|
|
899
713
|
|
900
714
|
>>> a = Agent(name = "Steve", traits = {"age": 10, "hair": "brown", "height": 5.5})
|
901
715
|
>>> a.to_dict()
|
902
|
-
{'traits': {'age': 10, 'hair': 'brown', 'height': 5.5}, '
|
903
|
-
|
904
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5}, instruction = "Have fun.")
|
905
|
-
>>> a.to_dict()
|
906
|
-
{'traits': {'age': 10, 'hair': 'brown', 'height': 5.5}, 'instruction': 'Have fun.', 'edsl_version': '...', 'edsl_class_name': 'Agent'}
|
716
|
+
{'name': 'Steve', 'traits': {'age': 10, 'hair': 'brown', 'height': 5.5}, 'edsl_version': '...', 'edsl_class_name': 'Agent'}
|
907
717
|
"""
|
908
|
-
d =
|
909
|
-
d["traits"] = copy.deepcopy(dict(self._traits))
|
910
|
-
if self.name:
|
911
|
-
d["name"] = self.name
|
912
|
-
if self.set_instructions:
|
913
|
-
d["instruction"] = self.instruction
|
914
|
-
if self.set_traits_presentation_template:
|
915
|
-
d["traits_presentation_template"] = self.traits_presentation_template
|
916
|
-
if self.codebook:
|
917
|
-
d["codebook"] = self.codebook
|
718
|
+
d = copy.deepcopy(self.data)
|
918
719
|
if add_edsl_version:
|
919
720
|
from edsl import __version__
|
920
721
|
|
@@ -934,18 +735,7 @@ class Agent(Base):
|
|
934
735
|
Agent(name = \"""Steve\""", traits = {'age': 10, 'hair': 'brown', 'height': 5.5})
|
935
736
|
|
936
737
|
"""
|
937
|
-
|
938
|
-
return cls(
|
939
|
-
traits=agent_dict["traits"],
|
940
|
-
name=agent_dict.get("name", None),
|
941
|
-
instruction=agent_dict.get("instruction", None),
|
942
|
-
traits_presentation_template=agent_dict.get(
|
943
|
-
"traits_presentation_template", None
|
944
|
-
),
|
945
|
-
codebook=agent_dict.get("codebook", None),
|
946
|
-
)
|
947
|
-
else: # old-style agent - we used to only store the traits
|
948
|
-
return cls(**agent_dict)
|
738
|
+
return cls(**agent_dict)
|
949
739
|
|
950
740
|
def _table(self) -> tuple[dict, list]:
|
951
741
|
"""Prepare generic table data."""
|
@@ -956,15 +746,10 @@ class Agent(Base):
|
|
956
746
|
return table_data, column_names
|
957
747
|
|
958
748
|
def add_trait(self, trait_name_or_dict: str, value: Optional[Any] = None) -> Agent:
|
959
|
-
"""Adds a trait to an agent and returns that agent
|
960
|
-
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
961
|
-
>>> a.add_trait("weight", 150)
|
962
|
-
Agent(traits = {'age': 10, 'hair': 'brown', 'height': 5.5, 'weight': 150})
|
963
|
-
"""
|
749
|
+
"""Adds a trait to an agent and returns that agent"""
|
964
750
|
if isinstance(trait_name_or_dict, dict) and value is None:
|
965
|
-
|
966
|
-
|
967
|
-
return newagent
|
751
|
+
self.traits.update(trait_name_or_dict)
|
752
|
+
return self
|
968
753
|
|
969
754
|
if isinstance(trait_name_or_dict, dict) and value:
|
970
755
|
raise AgentErrors(
|
@@ -972,9 +757,9 @@ class Agent(Base):
|
|
972
757
|
)
|
973
758
|
|
974
759
|
if isinstance(trait_name_or_dict, str):
|
975
|
-
|
976
|
-
|
977
|
-
return
|
760
|
+
trait = trait_name_or_dict
|
761
|
+
self.traits[trait] = value
|
762
|
+
return self
|
978
763
|
|
979
764
|
raise AgentErrors("Something is not right with adding a trait to an Agent")
|
980
765
|
|
@@ -987,9 +772,8 @@ class Agent(Base):
|
|
987
772
|
>>> a.remove_trait("age")
|
988
773
|
Agent(traits = {'hair': 'brown', 'height': 5.5})
|
989
774
|
"""
|
990
|
-
|
991
|
-
|
992
|
-
return newagent
|
775
|
+
_ = self.traits.pop(trait)
|
776
|
+
return self
|
993
777
|
|
994
778
|
def translate_traits(self, values_codebook: dict) -> Agent:
|
995
779
|
"""Translate traits to a new codebook.
|
@@ -1000,15 +784,32 @@ class Agent(Base):
|
|
1000
784
|
|
1001
785
|
:param values_codebook: The new codebook.
|
1002
786
|
"""
|
1003
|
-
new_traits = {}
|
1004
787
|
for key, value in self.traits.items():
|
1005
788
|
if key in values_codebook:
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
789
|
+
self.traits[key] = values_codebook[key][value]
|
790
|
+
return self
|
791
|
+
|
792
|
+
def rich_print(self):
|
793
|
+
"""Display an object as a rich table.
|
794
|
+
|
795
|
+
Example usage:
|
796
|
+
|
797
|
+
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
798
|
+
>>> a.rich_print()
|
799
|
+
<rich.table.Table object at ...>
|
800
|
+
"""
|
801
|
+
from rich.table import Table
|
802
|
+
|
803
|
+
table_data, column_names = self._table()
|
804
|
+
table = Table(title=f"{self.__class__.__name__} Attributes")
|
805
|
+
for column in column_names:
|
806
|
+
table.add_column(column, style="bold")
|
807
|
+
|
808
|
+
for row in table_data:
|
809
|
+
row_data = [row[column] for column in column_names]
|
810
|
+
table.add_row(*row_data)
|
811
|
+
|
812
|
+
return table
|
1012
813
|
|
1013
814
|
@classmethod
|
1014
815
|
def example(cls, randomize: bool = False) -> Agent:
|
@@ -1016,9 +817,6 @@ class Agent(Base):
|
|
1016
817
|
Returns an example Agent instance.
|
1017
818
|
|
1018
819
|
:param randomize: If True, adds a random string to the value of an example key.
|
1019
|
-
|
1020
|
-
>>> Agent.example()
|
1021
|
-
Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})
|
1022
820
|
"""
|
1023
821
|
addition = "" if not randomize else str(uuid4())
|
1024
822
|
return cls(traits={"age": 22, "hair": f"brown{addition}", "height": 5.5})
|
@@ -1030,12 +828,10 @@ class Agent(Base):
|
|
1030
828
|
|
1031
829
|
>>> a = Agent(traits = {"age": 10, "hair": "brown", "height": 5.5})
|
1032
830
|
>>> print(a.code())
|
1033
|
-
from edsl
|
831
|
+
from edsl import Agent
|
1034
832
|
agent = Agent(traits={'age': 10, 'hair': 'brown', 'height': 5.5})
|
1035
833
|
"""
|
1036
|
-
return (
|
1037
|
-
f"from edsl.agents.Agent import Agent\nagent = Agent(traits={self.traits})"
|
1038
|
-
)
|
834
|
+
return f"from edsl import Agent\nagent = Agent(traits={self.traits})"
|
1039
835
|
|
1040
836
|
|
1041
837
|
def main():
|