edsl 0.1.47__py3-none-any.whl → 0.1.48__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 (314) hide show
  1. edsl/__init__.py +44 -39
  2. edsl/__version__.py +1 -1
  3. edsl/agents/__init__.py +4 -2
  4. edsl/agents/{Agent.py → agent.py} +442 -152
  5. edsl/agents/{AgentList.py → agent_list.py} +220 -162
  6. edsl/agents/descriptors.py +46 -7
  7. edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
  8. edsl/base/__init__.py +75 -0
  9. edsl/base/base_class.py +1303 -0
  10. edsl/base/data_transfer_models.py +114 -0
  11. edsl/base/enums.py +215 -0
  12. edsl/base.py +8 -0
  13. edsl/buckets/__init__.py +25 -0
  14. edsl/buckets/bucket_collection.py +324 -0
  15. edsl/buckets/model_buckets.py +206 -0
  16. edsl/buckets/token_bucket.py +502 -0
  17. edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
  18. edsl/buckets/token_bucket_client.py +509 -0
  19. edsl/caching/__init__.py +20 -0
  20. edsl/caching/cache.py +814 -0
  21. edsl/caching/cache_entry.py +427 -0
  22. edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
  23. edsl/caching/exceptions.py +24 -0
  24. edsl/caching/orm.py +30 -0
  25. edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
  26. edsl/caching/sql_dict.py +441 -0
  27. edsl/config/__init__.py +8 -0
  28. edsl/config/config_class.py +177 -0
  29. edsl/config.py +4 -176
  30. edsl/conversation/Conversation.py +7 -7
  31. edsl/conversation/car_buying.py +4 -4
  32. edsl/conversation/chips.py +6 -6
  33. edsl/coop/__init__.py +25 -2
  34. edsl/coop/coop.py +303 -67
  35. edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
  36. edsl/coop/exceptions.py +62 -0
  37. edsl/coop/price_fetcher.py +126 -0
  38. edsl/coop/utils.py +89 -24
  39. edsl/data_transfer_models.py +5 -72
  40. edsl/dataset/__init__.py +10 -0
  41. edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
  42. edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
  43. edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
  44. edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
  45. edsl/{results → dataset/display}/table_renderers.py +58 -2
  46. edsl/{results → dataset}/file_exports.py +4 -5
  47. edsl/{results → dataset}/smart_objects.py +2 -2
  48. edsl/enums.py +5 -205
  49. edsl/inference_services/__init__.py +5 -0
  50. edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
  51. edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
  52. edsl/inference_services/data_structures.py +3 -2
  53. edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
  54. edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
  55. edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
  56. edsl/inference_services/registry.py +4 -41
  57. edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
  58. edsl/inference_services/services/__init__.py +31 -0
  59. edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
  60. edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
  61. edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
  62. edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
  63. edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
  64. edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
  65. edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
  66. edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
  67. edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
  68. edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
  69. edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
  70. edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
  71. edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
  72. edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
  73. edsl/inference_services/write_available.py +1 -2
  74. edsl/instructions/__init__.py +6 -0
  75. edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
  76. edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
  77. edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
  78. edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
  79. edsl/interviews/__init__.py +4 -0
  80. edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
  81. edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
  82. edsl/interviews/interview.py +638 -0
  83. edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
  84. edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
  85. edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
  86. edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
  87. edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
  88. edsl/invigilators/__init__.py +38 -0
  89. edsl/invigilators/invigilator_base.py +477 -0
  90. edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
  91. edsl/invigilators/prompt_constructor.py +476 -0
  92. edsl/{agents → invigilators}/prompt_helpers.py +2 -1
  93. edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
  94. edsl/{agents → invigilators}/question_option_processor.py +96 -21
  95. edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
  96. edsl/jobs/__init__.py +7 -1
  97. edsl/jobs/async_interview_runner.py +99 -35
  98. edsl/jobs/check_survey_scenario_compatibility.py +7 -5
  99. edsl/jobs/data_structures.py +153 -22
  100. edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
  101. edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
  102. edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
  103. edsl/jobs/{Jobs.py → jobs.py} +313 -167
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
  106. edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
  107. edsl/jobs/jobs_pricing_estimation.py +347 -0
  108. edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
  109. edsl/jobs/jobs_runner_asyncio.py +282 -0
  110. edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
  111. edsl/jobs/results_exceptions_handler.py +2 -2
  112. edsl/key_management/__init__.py +28 -0
  113. edsl/key_management/key_lookup.py +161 -0
  114. edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
  115. edsl/key_management/key_lookup_collection.py +82 -0
  116. edsl/key_management/models.py +218 -0
  117. edsl/language_models/__init__.py +7 -2
  118. edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
  119. edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
  120. edsl/language_models/language_model.py +1080 -0
  121. edsl/language_models/model.py +10 -25
  122. edsl/language_models/{ModelList.py → model_list.py} +9 -14
  123. edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
  124. edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
  125. edsl/language_models/repair.py +4 -4
  126. edsl/language_models/utilities.py +4 -4
  127. edsl/notebooks/__init__.py +3 -1
  128. edsl/notebooks/{Notebook.py → notebook.py} +7 -8
  129. edsl/prompts/__init__.py +1 -1
  130. edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
  131. edsl/prompts/{Prompt.py → prompt.py} +101 -95
  132. edsl/questions/HTMLQuestion.py +1 -1
  133. edsl/questions/__init__.py +154 -25
  134. edsl/questions/answer_validator_mixin.py +1 -1
  135. edsl/questions/compose_questions.py +4 -3
  136. edsl/questions/derived/question_likert_five.py +166 -0
  137. edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
  138. edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
  139. edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
  140. edsl/questions/descriptors.py +24 -30
  141. edsl/questions/loop_processor.py +65 -19
  142. edsl/questions/question_base.py +881 -0
  143. edsl/questions/question_base_gen_mixin.py +15 -16
  144. edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
  145. edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
  146. edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
  147. edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
  148. edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
  149. edsl/questions/question_free_text.py +282 -0
  150. edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
  151. edsl/questions/{QuestionList.py → question_list.py} +6 -7
  152. edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
  153. edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
  154. edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
  155. edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
  156. edsl/questions/question_registry.py +4 -9
  157. edsl/questions/register_questions_meta.py +8 -4
  158. edsl/questions/response_validator_abc.py +17 -16
  159. edsl/results/__init__.py +4 -1
  160. edsl/{exceptions/results.py → results/exceptions.py} +1 -1
  161. edsl/results/report.py +197 -0
  162. edsl/results/{Result.py → result.py} +131 -45
  163. edsl/results/{Results.py → results.py} +365 -220
  164. edsl/results/results_selector.py +344 -25
  165. edsl/scenarios/__init__.py +30 -3
  166. edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
  167. edsl/scenarios/directory_scanner.py +156 -13
  168. edsl/scenarios/document_chunker.py +186 -0
  169. edsl/scenarios/exceptions.py +101 -0
  170. edsl/scenarios/file_methods.py +2 -3
  171. edsl/scenarios/{FileStore.py → file_store.py} +275 -189
  172. edsl/scenarios/handlers/__init__.py +14 -14
  173. edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
  174. edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
  175. edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
  176. edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
  177. edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
  178. edsl/scenarios/handlers/latex_file_store.py +5 -0
  179. edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
  180. edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
  181. edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
  182. edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
  183. edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
  184. edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
  185. edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
  186. edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
  187. edsl/scenarios/scenario.py +928 -0
  188. edsl/scenarios/scenario_join.py +18 -5
  189. edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
  190. edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
  191. edsl/scenarios/scenario_selector.py +5 -1
  192. edsl/study/ObjectEntry.py +2 -2
  193. edsl/study/SnapShot.py +5 -5
  194. edsl/study/Study.py +18 -19
  195. edsl/study/__init__.py +6 -4
  196. edsl/surveys/__init__.py +7 -4
  197. edsl/surveys/dag/__init__.py +2 -0
  198. edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
  199. edsl/surveys/{DAG.py → dag/dag.py} +13 -10
  200. edsl/surveys/descriptors.py +1 -1
  201. edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
  202. edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
  203. edsl/surveys/memory/__init__.py +3 -0
  204. edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
  205. edsl/surveys/rules/__init__.py +3 -0
  206. edsl/surveys/{Rule.py → rules/rule.py} +103 -43
  207. edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
  208. edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
  209. edsl/surveys/survey.py +1743 -0
  210. edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
  211. edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
  212. edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
  213. edsl/tasks/__init__.py +32 -0
  214. edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
  215. edsl/tasks/task_creators.py +135 -0
  216. edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
  217. edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
  218. edsl/tasks/task_status_log.py +85 -0
  219. edsl/tokens/__init__.py +2 -0
  220. edsl/tokens/interview_token_usage.py +53 -0
  221. edsl/utilities/PrettyList.py +1 -1
  222. edsl/utilities/SystemInfo.py +25 -22
  223. edsl/utilities/__init__.py +29 -21
  224. edsl/utilities/gcp_bucket/__init__.py +2 -0
  225. edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
  226. edsl/utilities/interface.py +44 -536
  227. edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
  228. edsl/utilities/repair_functions.py +1 -1
  229. {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/METADATA +1 -1
  230. edsl-0.1.48.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -493
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/coop/PriceFetcher.py +0 -54
  236. edsl/data/Cache.py +0 -582
  237. edsl/data/CacheEntry.py +0 -238
  238. edsl/data/SQLiteDict.py +0 -292
  239. edsl/data/__init__.py +0 -5
  240. edsl/data/orm.py +0 -10
  241. edsl/exceptions/cache.py +0 -5
  242. edsl/exceptions/coop.py +0 -14
  243. edsl/exceptions/data.py +0 -14
  244. edsl/exceptions/scenarios.py +0 -29
  245. edsl/jobs/Answers.py +0 -43
  246. edsl/jobs/JobsPrompts.py +0 -354
  247. edsl/jobs/buckets/BucketCollection.py +0 -134
  248. edsl/jobs/buckets/ModelBuckets.py +0 -65
  249. edsl/jobs/buckets/TokenBucket.py +0 -283
  250. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  251. edsl/jobs/interviews/Interview.py +0 -395
  252. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  253. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  254. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  255. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  256. edsl/jobs/tasks/TaskCreators.py +0 -64
  257. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  258. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  259. edsl/language_models/LanguageModel.py +0 -635
  260. edsl/language_models/ServiceDataSources.py +0 -0
  261. edsl/language_models/key_management/KeyLookup.py +0 -63
  262. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  263. edsl/language_models/key_management/models.py +0 -137
  264. edsl/questions/QuestionBase.py +0 -544
  265. edsl/questions/QuestionFreeText.py +0 -130
  266. edsl/questions/derived/QuestionLikertFive.py +0 -76
  267. edsl/results/ResultsExportMixin.py +0 -45
  268. edsl/results/TextEditor.py +0 -50
  269. edsl/results/results_fetch_mixin.py +0 -33
  270. edsl/results/results_tools_mixin.py +0 -98
  271. edsl/scenarios/DocumentChunker.py +0 -104
  272. edsl/scenarios/Scenario.py +0 -548
  273. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  274. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  275. edsl/scenarios/handlers/latex.py +0 -5
  276. edsl/shared.py +0 -1
  277. edsl/surveys/Survey.py +0 -1301
  278. edsl/surveys/SurveyQualtricsImport.py +0 -284
  279. edsl/surveys/SurveyToApp.py +0 -141
  280. edsl/surveys/instructions/__init__.py +0 -0
  281. edsl/tools/__init__.py +0 -1
  282. edsl/tools/clusters.py +0 -192
  283. edsl/tools/embeddings.py +0 -27
  284. edsl/tools/embeddings_plotting.py +0 -118
  285. edsl/tools/plotting.py +0 -112
  286. edsl/tools/summarize.py +0 -18
  287. edsl/utilities/data/Registry.py +0 -6
  288. edsl/utilities/data/__init__.py +0 -1
  289. edsl/utilities/data/scooter_results.json +0 -1
  290. edsl-0.1.47.dist-info/RECORD +0 -354
  291. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  292. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  293. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  294. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  295. /edsl/{results → dataset/display}/table_display.css +0 -0
  296. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  297. /edsl/{results → dataset}/tree_explore.py +0 -0
  298. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  299. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  300. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  301. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  302. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  303. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  304. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  305. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  306. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  307. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  308. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  309. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  310. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  311. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  312. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  313. {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -4,26 +4,33 @@
4
4
  from __future__ import annotations
5
5
  import csv
6
6
  import sys
7
+ import random
8
+ import logging
9
+ from collections import defaultdict
10
+ from itertools import product
11
+
12
+
7
13
  from collections import UserList
8
14
  from collections.abc import Iterable
9
-
10
15
  from typing import Any, List, Optional, Union, TYPE_CHECKING
11
16
 
12
17
  from simpleeval import EvalWithCompoundTypes, NameNotDefined
13
18
 
14
- from edsl.Base import Base
15
- from edsl.utilities.remove_edsl_version import remove_edsl_version
16
- from edsl.exceptions.agents import AgentListError
17
- from edsl.utilities.is_notebook import is_notebook
18
- from edsl.results.ResultsExportMixin import ResultsExportMixin
19
- import logging
19
+ from ..base import Base
20
+ from ..utilities import is_notebook, remove_edsl_version, dict_hash
21
+ from ..dataset.dataset_operations_mixin import AgentListOperationsMixin
22
+
23
+ from .agent import Agent
24
+
25
+ from .exceptions import AgentListError
20
26
 
21
27
  logger = logging.getLogger(__name__)
22
28
 
23
29
  if TYPE_CHECKING:
24
- from edsl.scenarios.ScenarioList import ScenarioList
25
- from edsl.agents.Agent import Agent
30
+ from ..scenarios import ScenarioList
31
+ from ..agents import Agent
26
32
  from pandas import DataFrame
33
+ from ..dataset import Dataset
27
34
 
28
35
 
29
36
  def is_iterable(obj):
@@ -36,30 +43,66 @@ class EmptyAgentList:
36
43
 
37
44
 
38
45
  # ResultsExportMixin,
39
- class AgentList(UserList, Base):
40
- """A list of Agents."""
46
+ class AgentList(UserList, Base, AgentListOperationsMixin):
47
+ """A list of Agents with additional functionality for manipulation and analysis.
48
+
49
+ The AgentList class extends Python's UserList to provide a container for Agent objects
50
+ with methods for filtering, transforming, and analyzing collections of agents.
51
+
52
+
53
+ >>> AgentList.example().to_scenario_list()
54
+ ScenarioList([Scenario({'age': 22, 'hair': 'brown', 'height': 5.5}), Scenario({'age': 22, 'hair': 'brown', 'height': 5.5})])
55
+
56
+ >>> AgentList.example().to_dataset()
57
+ Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}])
58
+
59
+ >>> AgentList.example().to_pandas()
60
+ age hair height
61
+ 0 22 brown 5.5
62
+ 1 22 brown 5.5
63
+
64
+
65
+ """
41
66
 
42
67
  __documentation__ = (
43
68
  "https://docs.expectedparrot.com/en/latest/agents.html#agentlist-class"
44
69
  )
45
70
 
46
- def __init__(self, data: Optional[list["Agent"]] = None):
71
+ def __init__(self, data: Optional[list["Agent"]] = None, codebook: Optional[dict[str, str]] = None):
47
72
  """Initialize a new AgentList.
48
73
 
49
- :param data: A list of Agents.
74
+ >>> from edsl import Agent
75
+ >>> al = AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}),
76
+ ... Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
77
+ >>> al
78
+ AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
79
+ >>> al_with_codebook = AgentList([Agent(traits = {'age': 22})], codebook={'age': 'Age in years'})
80
+ >>> al_with_codebook[0].codebook
81
+ {'age': 'Age in years'}
82
+
83
+ Args:
84
+ data: A list of Agent objects. If None, creates an empty AgentList.
85
+ codebook: Optional dictionary mapping trait names to descriptions.
86
+ If provided, will be applied to all agents in the list.
50
87
  """
51
88
  if data is not None:
52
89
  super().__init__(data)
53
90
  else:
54
91
  super().__init__()
92
+
93
+ # Apply codebook to all agents if provided
94
+ if codebook is not None:
95
+ self.set_codebook(codebook)
55
96
 
56
97
  def shuffle(self, seed: Optional[str] = None) -> AgentList:
57
- """Shuffle the AgentList.
98
+ """Randomly shuffle the agents in place.
58
99
 
59
- :param seed: The seed for the random number generator.
60
- """
61
- import random
100
+ Args:
101
+ seed: Optional seed for the random number generator to ensure reproducibility.
62
102
 
103
+ Returns:
104
+ AgentList: The shuffled AgentList (self).
105
+ """
63
106
  if seed is not None:
64
107
  random.seed(seed)
65
108
  random.shuffle(self.data)
@@ -68,64 +111,52 @@ class AgentList(UserList, Base):
68
111
  def sample(self, n: int, seed: Optional[str] = None) -> AgentList:
69
112
  """Return a random sample of agents.
70
113
 
71
- :param n: The number of agents to sample.
72
- :param seed: The seed for the random number generator.
114
+ Args:
115
+ n: The number of agents to sample.
116
+ seed: Optional seed for the random number generator to ensure reproducibility.
117
+
118
+ Returns:
119
+ AgentList: A new AgentList containing the sampled agents.
73
120
  """
74
- import random
75
121
 
76
122
  if seed:
77
123
  random.seed(seed)
78
124
  return AgentList(random.sample(self.data, n))
79
125
 
80
- def to_pandas(self) -> "DataFrame":
81
- """Return a pandas DataFrame.
82
-
83
- >>> from edsl.agents.Agent import Agent
84
- >>> al = AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
85
- >>> al.to_pandas()
86
- age hair height
87
- 0 22 brown 5.5
88
- 1 22 brown 5.5
89
- """
90
- return self.to_scenario_list().to_pandas()
91
-
92
- def tally(
93
- self, *fields: Optional[str], top_n: Optional[int] = None, output="Dataset"
94
- ) -> Union[dict, "Dataset"]:
95
- """Tally the values of a field or perform a cross-tab of multiple fields.
126
+ def duplicate(self) -> AgentList:
127
+ """Create a deep copy of the AgentList.
96
128
 
97
- :param fields: The field(s) to tally, multiple fields for cross-tabulation.
129
+ Returns:
130
+ AgentList: A new AgentList containing copies of all agents.
98
131
 
99
- >>> al = AgentList.example()
100
- >>> al.tally('age')
101
- Dataset([{'age': [22]}, {'count': [2]}])
102
- """
103
- return self.to_scenario_list().tally(*fields, top_n=top_n, output=output)
104
-
105
- def duplicate(self):
106
- """Duplicate the AgentList.
107
-
108
- >>> al = AgentList.example()
109
- >>> al2 = al.duplicate()
110
- >>> al2 == al
111
- True
112
- >>> id(al2) == id(al)
113
- False
132
+ Examples:
133
+ >>> al = AgentList.example()
134
+ >>> al2 = al.duplicate()
135
+ >>> al2 == al
136
+ True
137
+ >>> id(al2) == id(al)
138
+ False
114
139
  """
115
140
  return AgentList([a.duplicate() for a in self.data])
116
141
 
117
- def rename(self, old_name, new_name) -> AgentList:
118
- """Rename a trait in the AgentList.
142
+ def rename(self, old_name: str, new_name: str) -> AgentList:
143
+ """Rename a trait across all agents in the list.
119
144
 
120
- :param old_name: The old name of the trait.
121
- :param new_name: The new name of the trait.
122
- :param inplace: Whether to rename the trait in place.
145
+ Args:
146
+ old_name: The current name of the trait.
147
+ new_name: The new name to assign to the trait.
123
148
 
124
- >>> from edsl.agents.Agent import Agent
125
- >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
126
- >>> al2 = al.rename('a', 'c')
127
- >>> assert al2 == AgentList([Agent(traits = {'c': 1, 'b': 1}), Agent(traits = {'c': 1, 'b': 2})])
128
- >>> assert al != al2
149
+ Returns:
150
+ AgentList: A new AgentList with the renamed trait.
151
+
152
+ Examples:
153
+ >>> from edsl import Agent
154
+ >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}),
155
+ ... Agent(traits = {'a': 1, 'b': 2})])
156
+ >>> al2 = al.rename('a', 'c')
157
+ >>> assert al2 == AgentList([Agent(traits = {'c': 1, 'b': 1}),
158
+ ... Agent(traits = {'c': 1, 'b': 2})])
159
+ >>> assert al != al2
129
160
  """
130
161
  newagents = []
131
162
  for agent in self:
@@ -133,15 +164,21 @@ class AgentList(UserList, Base):
133
164
  return AgentList(newagents)
134
165
 
135
166
  def select(self, *traits) -> AgentList:
136
- """Selects agents with only the references traits.
167
+ """Create a new AgentList with only the specified traits.
137
168
 
138
- >>> from edsl.agents.Agent import Agent
139
- >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
140
- >>> al.select('a')
141
- AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
169
+ Args:
170
+ *traits: Variable number of trait names to keep.
142
171
 
143
- """
172
+ Returns:
173
+ AgentList: A new AgentList containing agents with only the selected traits.
144
174
 
175
+ Examples:
176
+ >>> from edsl import Agent
177
+ >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}),
178
+ ... Agent(traits = {'a': 1, 'b': 2})])
179
+ >>> al.select('a')
180
+ AgentList([Agent(traits = {'a': 1}), Agent(traits = {'a': 1})])
181
+ """
145
182
  if len(traits) == 1:
146
183
  traits_to_select = [list(traits)[0]]
147
184
  else:
@@ -150,23 +187,27 @@ class AgentList(UserList, Base):
150
187
  return AgentList([agent.select(*traits_to_select) for agent in self.data])
151
188
 
152
189
  def filter(self, expression: str) -> AgentList:
153
- """
154
- Filter a list of agents based on an expression.
190
+ """Filter agents based on a boolean expression.
191
+
192
+ Args:
193
+ expression: A string containing a boolean expression to evaluate against
194
+ each agent's traits.
155
195
 
156
- >>> from edsl.agents.Agent import Agent
157
- >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}), Agent(traits = {'a': 1, 'b': 2})])
158
- >>> al.filter("b == 2")
159
- AgentList([Agent(traits = {'a': 1, 'b': 2})])
196
+ Returns:
197
+ AgentList: A new AgentList containing only agents that satisfy the expression.
198
+
199
+ Examples:
200
+ >>> from edsl import Agent
201
+ >>> al = AgentList([Agent(traits = {'a': 1, 'b': 1}),
202
+ ... Agent(traits = {'a': 1, 'b': 2})])
203
+ >>> al.filter("b == 2")
204
+ AgentList([Agent(traits = {'a': 1, 'b': 2})])
160
205
  """
161
206
 
162
207
  def create_evaluator(agent: "Agent"):
163
- """Create an evaluator for the given result.
164
- The 'combined_dict' is a mapping of all values for that Result object.
165
- """
208
+ """Create an evaluator for the given agent."""
166
209
  return EvalWithCompoundTypes(names=agent.traits)
167
210
 
168
- # iterates through all the results and evaluates the expression
169
-
170
211
  try:
171
212
  new_data = [
172
213
  agent for agent in self.data if create_evaluator(agent).eval(expression)
@@ -188,7 +229,7 @@ class AgentList(UserList, Base):
188
229
  @property
189
230
  def all_traits(self) -> list[str]:
190
231
  """Return all traits in the AgentList.
191
- >>> from edsl.agents.Agent import Agent
232
+ >>> from edsl import Agent
192
233
  >>> agent_1 = Agent(traits = {'age': 22})
193
234
  >>> agent_2 = Agent(traits = {'hair': 'brown'})
194
235
  >>> al = AgentList([agent_1, agent_2])
@@ -201,7 +242,7 @@ class AgentList(UserList, Base):
201
242
  return list(d.keys())
202
243
 
203
244
  @classmethod
204
- def from_csv(cls, file_path: str, name_field: Optional[str] = None):
245
+ def from_csv(cls, file_path: str, name_field: Optional[str] = None, codebook: Optional[dict[str, str]] = None):
205
246
  """Load AgentList from a CSV file.
206
247
 
207
248
  >>> import csv
@@ -216,12 +257,16 @@ class AgentList(UserList, Base):
216
257
  >>> al = AgentList.from_csv('/tmp/agents.csv', name_field='hair')
217
258
  >>> al
218
259
  AgentList([Agent(name = \"""brown\""", traits = {'age': '22', 'height': '5.5'})])
260
+ >>> al = AgentList.from_csv('/tmp/agents.csv', codebook={'age': 'Age in years'})
261
+ >>> al[0].codebook
262
+ {'age': 'Age in years'}
219
263
  >>> os.remove('/tmp/agents.csv')
220
264
 
221
265
  :param file_path: The path to the CSV file.
222
266
  :param name_field: The name of the field to use as the agent name.
267
+ :param codebook: Optional dictionary mapping trait names to descriptions.
223
268
  """
224
- from edsl.agents.Agent import Agent
269
+ from .agent import Agent
225
270
 
226
271
  agent_list = []
227
272
  with open(file_path, "r") as f:
@@ -234,9 +279,9 @@ class AgentList(UserList, Base):
234
279
  name_field = "name"
235
280
  if name_field is not None:
236
281
  agent_name = row.pop(name_field)
237
- agent_list.append(Agent(traits=row, name=agent_name))
282
+ agent_list.append(Agent(traits=row, name=agent_name, codebook=codebook))
238
283
  else:
239
- agent_list.append(Agent(traits=row))
284
+ agent_list.append(Agent(traits=row, codebook=codebook))
240
285
  return cls(agent_list)
241
286
 
242
287
  def translate_traits(self, codebook: dict[str, str]):
@@ -258,7 +303,7 @@ class AgentList(UserList, Base):
258
303
  """Remove traits from the AgentList.
259
304
 
260
305
  :param traits: The traits to remove.
261
- >>> from edsl.agents.Agent import Agent
306
+ >>> from edsl import Agent
262
307
  >>> al = AgentList([Agent({'age': 22, 'hair': 'brown', 'height': 5.5}), Agent({'age': 22, 'hair': 'brown', 'height': 5.5})])
263
308
  >>> al.remove_trait('age')
264
309
  AgentList([Agent(traits = {'hair': 'brown', 'height': 5.5}), Agent(traits = {'hair': 'brown', 'height': 5.5})])
@@ -282,7 +327,7 @@ class AgentList(UserList, Base):
282
327
  >>> al.add_trait('new_trait', [1, 2, 3])
283
328
  Traceback (most recent call last):
284
329
  ...
285
- edsl.exceptions.agents.AgentListError: The passed values have to be the same length as the agent list.
330
+ edsl.agents.exceptions.AgentListError: The passed values have to be the same length as the agent list.
286
331
  ...
287
332
  """
288
333
  if not is_iterable(values):
@@ -322,8 +367,6 @@ class AgentList(UserList, Base):
322
367
  >>> hash(al)
323
368
  1681154913465662422
324
369
  """
325
- from edsl.utilities.utilities import dict_hash
326
-
327
370
  return dict_hash(self.to_dict(add_edsl_version=False, sorted=True))
328
371
 
329
372
  def to_dict(self, sorted=False, add_edsl_version=True):
@@ -331,7 +374,13 @@ class AgentList(UserList, Base):
331
374
 
332
375
  >>> AgentList.example().to_dict(add_edsl_version=False)
333
376
  {'agent_list': [{'traits': {'age': 22, 'hair': 'brown', 'height': 5.5}}, {'traits': {'age': 22, 'hair': 'brown', 'height': 5.5}}]}
334
-
377
+ >>> example_codebook = {'age': 'Age in years'}
378
+ >>> al = AgentList.example().set_codebook(example_codebook)
379
+ >>> result = al.to_dict(add_edsl_version=False)
380
+ >>> 'codebook' in result
381
+ True
382
+ >>> result['codebook'] == example_codebook
383
+ True
335
384
  """
336
385
  if sorted:
337
386
  data = self.data[:]
@@ -344,6 +393,19 @@ class AgentList(UserList, Base):
344
393
  agent.to_dict(add_edsl_version=add_edsl_version) for agent in data
345
394
  ]
346
395
  }
396
+
397
+ # Add codebook if all agents have the same codebook
398
+ if len(self.data) > 0:
399
+ # Get the first agent's codebook
400
+ first_codebook = self.data[0].codebook
401
+
402
+ # Check if all agents have the same codebook
403
+ all_same = all(agent.codebook == first_codebook for agent in self.data)
404
+
405
+ # Only include codebook if it's non-empty and consistent across all agents
406
+ if all_same and first_codebook:
407
+ d["codebook"] = first_codebook
408
+
347
409
  if add_edsl_version:
348
410
  from edsl import __version__
349
411
 
@@ -360,15 +422,15 @@ class AgentList(UserList, Base):
360
422
  def __repr__(self):
361
423
  return f"AgentList({self.data})"
362
424
 
363
- def _summary(self):
425
+ def _summary(self) -> dict:
364
426
  return {
365
427
  "agents": len(self),
366
428
  }
367
-
429
+
368
430
  def set_codebook(self, codebook: dict[str, str]) -> AgentList:
369
431
  """Set the codebook for the AgentList.
370
432
 
371
- >>> from edsl.agents.Agent import Agent
433
+ >>> from edsl import Agent
372
434
  >>> a = Agent(traits = {'hair': 'brown'})
373
435
  >>> al = AgentList([a, a])
374
436
  >>> _ = al.set_codebook({'hair': "Color of hair on driver's license"})
@@ -383,51 +445,13 @@ class AgentList(UserList, Base):
383
445
 
384
446
  return self
385
447
 
386
- def to_csv(self, file_path: str):
387
- """Save the AgentList to a CSV file.
388
-
389
- :param file_path: The path to the CSV file.
390
- """
391
- self.to_scenario_list().to_csv(file_path)
392
-
393
- def to_list(self, include_agent_name=False) -> list[tuple]:
394
- """Return a list of tuples."""
395
- return self.to_scenario_list(include_agent_name).to_list()
396
-
397
- def to_scenario_list(
398
- self, include_agent_name: bool = False, include_instruction: bool = False
399
- ) -> ScenarioList:
400
- """Converts the agent to a scenario list."""
401
- from edsl.scenarios.ScenarioList import ScenarioList
402
- from edsl.scenarios.Scenario import Scenario
403
-
404
- # raise NotImplementedError("This method is not implemented yet.")
405
-
406
- scenario_list = ScenarioList()
407
- for agent in self.data:
408
- d = agent.traits
409
- if include_agent_name:
410
- d["agent_name"] = agent.name
411
- if include_instruction:
412
- d["instruction"] = agent.instruction
413
- scenario_list.append(Scenario(d))
414
- return scenario_list
415
-
416
- # if include_agent_name:
417
- # return ScenarioList(
418
- # [
419
- # Scenario(agent.traits | {"agent_name": agent.name} | })
420
- # for agent in self.data
421
- # ]
422
- # )
423
- # return ScenarioList([Scenario(agent.traits) for agent in self.data])
424
448
 
425
449
  def table(
426
450
  self,
427
451
  *fields,
428
452
  tablefmt: Optional[str] = None,
429
453
  pretty_labels: Optional[dict] = None,
430
- ) -> Table:
454
+ ) -> "Table":
431
455
  if len(self) == 0:
432
456
  e = AgentListError("Cannot create a table from an empty AgentList.")
433
457
  if is_notebook():
@@ -442,18 +466,24 @@ class AgentList(UserList, Base):
442
466
  )
443
467
 
444
468
  def to_dataset(self, traits_only: bool = True):
469
+ """Convert the AgentList to a Dataset.
470
+
471
+ Args:
472
+ traits_only: If True, only include agent traits. If False, also include
473
+ agent parameters like instructions and names.
474
+
475
+ Returns:
476
+ Dataset: A dataset containing the agents' traits and optionally their parameters.
477
+
478
+ Examples:
479
+ >>> from edsl import AgentList
480
+ >>> al = AgentList.example()
481
+ >>> al.to_dataset()
482
+ Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}])
483
+ >>> al.to_dataset(traits_only=False) # doctest: +NORMALIZE_WHITESPACE
484
+ Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}, {'agent_parameters': [{'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None}, {'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None}]}])
445
485
  """
446
- Convert the AgentList to a Dataset.
447
-
448
- >>> from edsl.agents.AgentList import AgentList
449
- >>> al = AgentList.example()
450
- >>> al.to_dataset()
451
- Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}])
452
- >>> al.to_dataset(traits_only = False)
453
- Dataset([{'age': [22, 22]}, {'hair': ['brown', 'brown']}, {'height': [5.5, 5.5]}, {'agent_parameters': [{'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None}, {'instruction': 'You are answering questions as if you were a human. Do not break character.', 'agent_name': None}]}])
454
- """
455
- from edsl.results.Dataset import Dataset
456
- from collections import defaultdict
486
+ from ..dataset import Dataset
457
487
 
458
488
  agent_trait_keys = []
459
489
  for agent in self:
@@ -472,55 +502,84 @@ class AgentList(UserList, Base):
472
502
  )
473
503
  return Dataset([{key: entry} for key, entry in data.items()])
474
504
 
475
- def tree(self, node_order: Optional[List[str]] = None):
476
- return self.to_scenario_list().tree(node_order)
477
-
478
505
  @classmethod
479
506
  @remove_edsl_version
480
507
  def from_dict(cls, data: dict) -> "AgentList":
481
508
  """Deserialize the dictionary back to an AgentList object.
482
509
 
483
510
  :param: data: A dictionary representing an AgentList.
484
- >>> from edsl.agents.Agent import Agent
511
+
512
+ >>> from edsl import Agent
485
513
  >>> al = AgentList([Agent.example(), Agent.example()])
486
514
  >>> al2 = AgentList.from_dict(al.to_dict())
487
515
  >>> al2 == al
488
516
  True
517
+ >>> example_codebook = {'age': 'Age in years'}
518
+ >>> al = AgentList([Agent.example()]).set_codebook(example_codebook)
519
+ >>> al2 = AgentList.from_dict(al.to_dict())
520
+ >>> al2[0].codebook == example_codebook
521
+ True
489
522
  """
490
- from edsl.agents.Agent import Agent
523
+ from .agent import Agent
491
524
 
492
525
  agents = [Agent.from_dict(agent_dict) for agent_dict in data["agent_list"]]
493
- return cls(agents)
526
+ agent_list = cls(agents)
527
+
528
+ # Apply codebook if present in the dictionary
529
+ if "codebook" in data and data["codebook"]:
530
+ agent_list.set_codebook(data["codebook"])
531
+
532
+ return agent_list
494
533
 
495
534
  @classmethod
496
- def example(cls, randomize: bool = False) -> AgentList:
535
+ def example(cls, randomize: bool = False, codebook: Optional[dict[str, str]] = None) -> AgentList:
497
536
  """
498
537
  Returns an example AgentList instance.
499
538
 
500
539
  :param randomize: If True, uses Agent's randomize method.
540
+ :param codebook: Optional dictionary mapping trait names to descriptions.
541
+
542
+ >>> al = AgentList.example()
543
+ >>> al
544
+ AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
545
+ >>> al = AgentList.example(codebook={'age': 'Age in years'})
546
+ >>> al[0].codebook
547
+ {'age': 'Age in years'}
501
548
  """
502
- from edsl.agents.Agent import Agent
549
+ from .agent import Agent
503
550
 
504
- return cls([Agent.example(randomize), Agent.example(randomize)])
551
+ agent_list = cls([Agent.example(randomize), Agent.example(randomize)])
552
+
553
+ if codebook:
554
+ agent_list.set_codebook(codebook)
555
+
556
+ return agent_list
505
557
 
506
558
  @classmethod
507
- def from_list(self, trait_name: str, values: List[Any]):
559
+ def from_list(self, trait_name: str, values: List[Any], codebook: Optional[dict[str, str]] = None) -> "AgentList":
508
560
  """Create an AgentList from a list of values.
509
561
 
510
562
  :param trait_name: The name of the trait.
511
563
  :param values: A list of values.
564
+ :param codebook: Optional dictionary mapping trait names to descriptions.
512
565
 
513
566
  >>> AgentList.from_list('age', [22, 23])
514
567
  AgentList([Agent(traits = {'age': 22}), Agent(traits = {'age': 23})])
568
+ >>> al = AgentList.from_list('age', [22], codebook={'age': 'Age in years'})
569
+ >>> al[0].codebook
570
+ {'age': 'Age in years'}
515
571
  """
516
- from edsl.agents.Agent import Agent
572
+ from .agent import Agent
517
573
 
518
- return AgentList([Agent({trait_name: value}) for value in values])
574
+ agent_list = AgentList([Agent({trait_name: value}) for value in values])
575
+
576
+ if codebook:
577
+ agent_list.set_codebook(codebook)
578
+
579
+ return agent_list
519
580
 
520
581
  def __mul__(self, other: AgentList) -> AgentList:
521
582
  """Takes the cross product of two AgentLists."""
522
- from itertools import product
523
-
524
583
  new_sl = []
525
584
  for s1, s2 in list(product(self, other)):
526
585
  new_sl.append(s1 + s2)
@@ -531,13 +590,13 @@ class AgentList(UserList, Base):
531
590
 
532
591
  >>> al = AgentList.example()
533
592
  >>> print(al.code())
534
- from edsl.agents.Agent import Agent
535
- from edsl.agents.AgentList import AgentList
593
+ from edsl import Agent
594
+ from edsl import AgentList
536
595
  agent_list = AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5}), Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
537
596
  """
538
597
  lines = [
539
- "from edsl.agents.Agent import Agent",
540
- "from edsl.agents.AgentList import AgentList",
598
+ "from edsl import Agent",
599
+ "from edsl import AgentList",
541
600
  ]
542
601
  lines.append(f"agent_list = AgentList({self.data})")
543
602
  if string:
@@ -547,5 +606,4 @@ class AgentList(UserList, Base):
547
606
 
548
607
  if __name__ == "__main__":
549
608
  import doctest
550
-
551
- doctest.testmod(optionflags=doctest.ELLIPSIS)
609
+ doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)