edsl 0.1.49__py3-none-any.whl → 0.1.51__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 (257) hide show
  1. edsl/__init__.py +124 -53
  2. edsl/__version__.py +1 -1
  3. edsl/agents/agent.py +21 -21
  4. edsl/agents/agent_list.py +2 -5
  5. edsl/agents/exceptions.py +119 -5
  6. edsl/base/__init__.py +10 -35
  7. edsl/base/base_class.py +71 -36
  8. edsl/base/base_exception.py +204 -0
  9. edsl/base/data_transfer_models.py +1 -1
  10. edsl/base/exceptions.py +94 -0
  11. edsl/buckets/__init__.py +15 -1
  12. edsl/buckets/bucket_collection.py +3 -4
  13. edsl/buckets/exceptions.py +107 -0
  14. edsl/buckets/model_buckets.py +1 -2
  15. edsl/buckets/token_bucket.py +11 -6
  16. edsl/buckets/token_bucket_api.py +27 -12
  17. edsl/buckets/token_bucket_client.py +9 -7
  18. edsl/caching/cache.py +12 -4
  19. edsl/caching/cache_entry.py +10 -9
  20. edsl/caching/exceptions.py +113 -7
  21. edsl/caching/remote_cache_sync.py +6 -7
  22. edsl/caching/sql_dict.py +20 -14
  23. edsl/cli.py +43 -0
  24. edsl/config/__init__.py +1 -1
  25. edsl/config/config_class.py +32 -6
  26. edsl/conversation/Conversation.py +8 -4
  27. edsl/conversation/car_buying.py +1 -3
  28. edsl/conversation/exceptions.py +58 -0
  29. edsl/conversation/mug_negotiation.py +2 -8
  30. edsl/coop/__init__.py +28 -6
  31. edsl/coop/coop.py +120 -29
  32. edsl/coop/coop_functions.py +1 -1
  33. edsl/coop/ep_key_handling.py +1 -1
  34. edsl/coop/exceptions.py +188 -9
  35. edsl/coop/price_fetcher.py +5 -8
  36. edsl/coop/utils.py +4 -6
  37. edsl/dataset/__init__.py +5 -4
  38. edsl/dataset/dataset.py +177 -86
  39. edsl/dataset/dataset_operations_mixin.py +98 -76
  40. edsl/dataset/dataset_tree.py +11 -7
  41. edsl/dataset/display/table_display.py +0 -2
  42. edsl/dataset/display/table_renderers.py +6 -4
  43. edsl/dataset/exceptions.py +125 -0
  44. edsl/dataset/file_exports.py +18 -11
  45. edsl/dataset/r/ggplot.py +13 -6
  46. edsl/display/__init__.py +27 -0
  47. edsl/display/core.py +147 -0
  48. edsl/display/plugin.py +189 -0
  49. edsl/display/utils.py +52 -0
  50. edsl/inference_services/__init__.py +9 -1
  51. edsl/inference_services/available_model_cache_handler.py +1 -1
  52. edsl/inference_services/available_model_fetcher.py +5 -6
  53. edsl/inference_services/data_structures.py +10 -7
  54. edsl/inference_services/exceptions.py +132 -1
  55. edsl/inference_services/inference_service_abc.py +2 -2
  56. edsl/inference_services/inference_services_collection.py +2 -6
  57. edsl/inference_services/registry.py +4 -3
  58. edsl/inference_services/service_availability.py +4 -3
  59. edsl/inference_services/services/anthropic_service.py +4 -1
  60. edsl/inference_services/services/aws_bedrock.py +13 -12
  61. edsl/inference_services/services/azure_ai.py +12 -10
  62. edsl/inference_services/services/deep_infra_service.py +1 -4
  63. edsl/inference_services/services/deep_seek_service.py +1 -5
  64. edsl/inference_services/services/google_service.py +7 -3
  65. edsl/inference_services/services/groq_service.py +1 -1
  66. edsl/inference_services/services/mistral_ai_service.py +4 -2
  67. edsl/inference_services/services/ollama_service.py +1 -1
  68. edsl/inference_services/services/open_ai_service.py +7 -5
  69. edsl/inference_services/services/perplexity_service.py +6 -2
  70. edsl/inference_services/services/test_service.py +8 -7
  71. edsl/inference_services/services/together_ai_service.py +2 -3
  72. edsl/inference_services/services/xai_service.py +1 -1
  73. edsl/instructions/__init__.py +1 -1
  74. edsl/instructions/change_instruction.py +7 -5
  75. edsl/instructions/exceptions.py +61 -0
  76. edsl/instructions/instruction.py +6 -2
  77. edsl/instructions/instruction_collection.py +6 -4
  78. edsl/instructions/instruction_handler.py +12 -15
  79. edsl/interviews/ReportErrors.py +0 -3
  80. edsl/interviews/__init__.py +9 -2
  81. edsl/interviews/answering_function.py +11 -13
  82. edsl/interviews/exception_tracking.py +15 -8
  83. edsl/interviews/exceptions.py +79 -0
  84. edsl/interviews/interview.py +33 -30
  85. edsl/interviews/interview_status_dictionary.py +4 -2
  86. edsl/interviews/interview_status_log.py +2 -1
  87. edsl/interviews/interview_task_manager.py +5 -5
  88. edsl/interviews/request_token_estimator.py +5 -2
  89. edsl/interviews/statistics.py +3 -4
  90. edsl/invigilators/__init__.py +7 -1
  91. edsl/invigilators/exceptions.py +79 -0
  92. edsl/invigilators/invigilator_base.py +0 -1
  93. edsl/invigilators/invigilators.py +9 -13
  94. edsl/invigilators/prompt_constructor.py +1 -5
  95. edsl/invigilators/prompt_helpers.py +8 -4
  96. edsl/invigilators/question_instructions_prompt_builder.py +1 -1
  97. edsl/invigilators/question_option_processor.py +9 -5
  98. edsl/invigilators/question_template_replacements_builder.py +3 -2
  99. edsl/jobs/__init__.py +42 -5
  100. edsl/jobs/async_interview_runner.py +25 -23
  101. edsl/jobs/check_survey_scenario_compatibility.py +11 -10
  102. edsl/jobs/data_structures.py +8 -5
  103. edsl/jobs/exceptions.py +177 -8
  104. edsl/jobs/fetch_invigilator.py +1 -1
  105. edsl/jobs/jobs.py +74 -69
  106. edsl/jobs/jobs_checks.py +6 -7
  107. edsl/jobs/jobs_component_constructor.py +4 -4
  108. edsl/jobs/jobs_pricing_estimation.py +4 -3
  109. edsl/jobs/jobs_remote_inference_logger.py +5 -4
  110. edsl/jobs/jobs_runner_asyncio.py +3 -4
  111. edsl/jobs/jobs_runner_status.py +8 -9
  112. edsl/jobs/remote_inference.py +27 -24
  113. edsl/jobs/results_exceptions_handler.py +10 -7
  114. edsl/key_management/__init__.py +3 -1
  115. edsl/key_management/exceptions.py +62 -0
  116. edsl/key_management/key_lookup.py +1 -1
  117. edsl/key_management/key_lookup_builder.py +37 -14
  118. edsl/key_management/key_lookup_collection.py +2 -0
  119. edsl/language_models/__init__.py +1 -1
  120. edsl/language_models/exceptions.py +302 -14
  121. edsl/language_models/language_model.py +9 -8
  122. edsl/language_models/model.py +4 -4
  123. edsl/language_models/model_list.py +1 -1
  124. edsl/language_models/price_manager.py +1 -1
  125. edsl/language_models/raw_response_handler.py +14 -9
  126. edsl/language_models/registry.py +17 -21
  127. edsl/language_models/repair.py +0 -6
  128. edsl/language_models/unused/fake_openai_service.py +0 -1
  129. edsl/load_plugins.py +69 -0
  130. edsl/logger.py +146 -0
  131. edsl/notebooks/__init__.py +24 -1
  132. edsl/notebooks/exceptions.py +82 -0
  133. edsl/notebooks/notebook.py +7 -3
  134. edsl/notebooks/notebook_to_latex.py +1 -2
  135. edsl/plugins/__init__.py +63 -0
  136. edsl/plugins/built_in/export_example.py +50 -0
  137. edsl/plugins/built_in/pig_latin.py +67 -0
  138. edsl/plugins/cli.py +372 -0
  139. edsl/plugins/cli_typer.py +283 -0
  140. edsl/plugins/exceptions.py +31 -0
  141. edsl/plugins/hookspec.py +51 -0
  142. edsl/plugins/plugin_host.py +128 -0
  143. edsl/plugins/plugin_manager.py +633 -0
  144. edsl/plugins/plugins_registry.py +168 -0
  145. edsl/prompts/__init__.py +24 -1
  146. edsl/prompts/exceptions.py +107 -5
  147. edsl/prompts/prompt.py +15 -7
  148. edsl/questions/HTMLQuestion.py +5 -11
  149. edsl/questions/Quick.py +0 -1
  150. edsl/questions/__init__.py +6 -4
  151. edsl/questions/answer_validator_mixin.py +318 -323
  152. edsl/questions/compose_questions.py +3 -3
  153. edsl/questions/descriptors.py +11 -50
  154. edsl/questions/exceptions.py +278 -22
  155. edsl/questions/loop_processor.py +7 -5
  156. edsl/questions/prompt_templates/question_list.jinja +3 -0
  157. edsl/questions/question_base.py +46 -19
  158. edsl/questions/question_base_gen_mixin.py +2 -2
  159. edsl/questions/question_base_prompts_mixin.py +13 -7
  160. edsl/questions/question_budget.py +503 -98
  161. edsl/questions/question_check_box.py +660 -160
  162. edsl/questions/question_dict.py +345 -194
  163. edsl/questions/question_extract.py +401 -61
  164. edsl/questions/question_free_text.py +80 -14
  165. edsl/questions/question_functional.py +119 -9
  166. edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
  167. edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
  168. edsl/questions/question_list.py +275 -28
  169. edsl/questions/question_matrix.py +643 -96
  170. edsl/questions/question_multiple_choice.py +219 -51
  171. edsl/questions/question_numerical.py +361 -32
  172. edsl/questions/question_rank.py +401 -124
  173. edsl/questions/question_registry.py +7 -5
  174. edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
  175. edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
  176. edsl/questions/register_questions_meta.py +2 -2
  177. edsl/questions/response_validator_abc.py +13 -15
  178. edsl/questions/response_validator_factory.py +10 -12
  179. edsl/questions/templates/dict/answering_instructions.jinja +1 -0
  180. edsl/questions/templates/rank/question_presentation.jinja +1 -1
  181. edsl/results/__init__.py +1 -1
  182. edsl/results/exceptions.py +141 -7
  183. edsl/results/report.py +1 -2
  184. edsl/results/result.py +11 -9
  185. edsl/results/results.py +480 -321
  186. edsl/results/results_selector.py +8 -4
  187. edsl/scenarios/PdfExtractor.py +2 -2
  188. edsl/scenarios/construct_download_link.py +69 -35
  189. edsl/scenarios/directory_scanner.py +33 -14
  190. edsl/scenarios/document_chunker.py +1 -1
  191. edsl/scenarios/exceptions.py +238 -14
  192. edsl/scenarios/file_methods.py +1 -1
  193. edsl/scenarios/file_store.py +7 -3
  194. edsl/scenarios/handlers/__init__.py +17 -0
  195. edsl/scenarios/handlers/docx_file_store.py +0 -5
  196. edsl/scenarios/handlers/pdf_file_store.py +0 -1
  197. edsl/scenarios/handlers/pptx_file_store.py +0 -5
  198. edsl/scenarios/handlers/py_file_store.py +0 -1
  199. edsl/scenarios/handlers/sql_file_store.py +1 -4
  200. edsl/scenarios/handlers/sqlite_file_store.py +0 -1
  201. edsl/scenarios/handlers/txt_file_store.py +1 -1
  202. edsl/scenarios/scenario.py +1 -3
  203. edsl/scenarios/scenario_list.py +179 -27
  204. edsl/scenarios/scenario_list_pdf_tools.py +1 -0
  205. edsl/scenarios/scenario_selector.py +0 -1
  206. edsl/surveys/__init__.py +3 -4
  207. edsl/surveys/dag/__init__.py +4 -2
  208. edsl/surveys/descriptors.py +1 -1
  209. edsl/surveys/edit_survey.py +1 -0
  210. edsl/surveys/exceptions.py +165 -9
  211. edsl/surveys/memory/__init__.py +5 -3
  212. edsl/surveys/memory/memory_management.py +1 -0
  213. edsl/surveys/memory/memory_plan.py +6 -15
  214. edsl/surveys/rules/__init__.py +5 -3
  215. edsl/surveys/rules/rule.py +1 -2
  216. edsl/surveys/rules/rule_collection.py +1 -1
  217. edsl/surveys/survey.py +12 -24
  218. edsl/surveys/survey_css.py +3 -3
  219. edsl/surveys/survey_export.py +6 -3
  220. edsl/surveys/survey_flow_visualization.py +10 -1
  221. edsl/surveys/survey_simulator.py +2 -1
  222. edsl/tasks/__init__.py +23 -1
  223. edsl/tasks/exceptions.py +72 -0
  224. edsl/tasks/question_task_creator.py +3 -3
  225. edsl/tasks/task_creators.py +1 -3
  226. edsl/tasks/task_history.py +8 -10
  227. edsl/tasks/task_status_log.py +1 -2
  228. edsl/tokens/__init__.py +29 -1
  229. edsl/tokens/exceptions.py +37 -0
  230. edsl/tokens/interview_token_usage.py +3 -2
  231. edsl/tokens/token_usage.py +4 -3
  232. edsl/utilities/__init__.py +21 -1
  233. edsl/utilities/decorators.py +1 -2
  234. edsl/utilities/markdown_to_docx.py +2 -2
  235. edsl/utilities/markdown_to_pdf.py +1 -1
  236. edsl/utilities/repair_functions.py +0 -1
  237. edsl/utilities/restricted_python.py +0 -1
  238. edsl/utilities/template_loader.py +2 -3
  239. edsl/utilities/utilities.py +8 -29
  240. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/METADATA +32 -2
  241. edsl-0.1.51.dist-info/RECORD +365 -0
  242. edsl-0.1.51.dist-info/entry_points.txt +3 -0
  243. edsl/dataset/smart_objects.py +0 -96
  244. edsl/exceptions/BaseException.py +0 -21
  245. edsl/exceptions/__init__.py +0 -54
  246. edsl/exceptions/configuration.py +0 -16
  247. edsl/exceptions/general.py +0 -34
  248. edsl/questions/derived/__init__.py +0 -0
  249. edsl/study/ObjectEntry.py +0 -173
  250. edsl/study/ProofOfWork.py +0 -113
  251. edsl/study/SnapShot.py +0 -80
  252. edsl/study/Study.py +0 -520
  253. edsl/study/__init__.py +0 -6
  254. edsl/utilities/interface.py +0 -135
  255. edsl-0.1.49.dist-info/RECORD +0 -347
  256. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/LICENSE +0 -0
  257. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/WHEEL +0 -0
edsl/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import time
3
+ import importlib
4
+ import pkgutil
3
5
 
4
6
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
5
7
  ROOT_DIR = os.path.dirname(BASE_DIR)
@@ -7,57 +9,126 @@ ROOT_DIR = os.path.dirname(BASE_DIR)
7
9
  from edsl.__version__ import __version__
8
10
  from edsl.config import Config, CONFIG
9
11
 
10
- __all__ = []
12
+ # Initialize and expose logger
13
+ from edsl import logger
14
+
15
+ # Set up logger with configuration from environment/config
16
+ # (We'll configure the logger after CONFIG is initialized below)
17
+
18
+ __all__ = ['logger']
19
+
20
+ # Define modules to import
21
+ modules_to_import = [
22
+ 'dataset',
23
+ 'agents',
24
+ 'surveys',
25
+ 'questions',
26
+ 'scenarios',
27
+ 'language_models',
28
+ 'results',
29
+ 'caching',
30
+ 'notebooks',
31
+ 'coop',
32
+ 'instructions',
33
+ 'jobs'
34
+ ]
35
+
36
+ # Dynamically import modules and extend __all__
37
+ for module_name in modules_to_import:
38
+ try:
39
+ # Import the module
40
+ module = importlib.import_module(f'.{module_name}', package='edsl')
41
+
42
+ # Get the module's __all__ attribute
43
+ module_all = getattr(module, '__all__', [])
44
+
45
+ # Import all names from the module
46
+ exec(f"from .{module_name} import *")
47
+
48
+ # Extend __all__ with the module's __all__
49
+ if module_all:
50
+ logger.debug(f"Adding {len(module_all)} items from {module_name} to __all__")
51
+ __all__.extend(module_all)
52
+ else:
53
+ logger.warning(f"Module {module_name} does not have __all__ defined")
54
+ except ImportError as e:
55
+ logger.warning(f"Failed to import module {module_name}: {e}")
56
+ except Exception as e:
57
+ logger.warning(f"Error importing from module {module_name}: {e}")
58
+
59
+
60
+ # Load plugins
61
+ try:
62
+ from edsl.load_plugins import load_plugins
63
+ from edsl.plugins import get_plugin_manager, get_exports
64
+
65
+ # Load all plugins
66
+ plugins = load_plugins()
67
+ logger.info(f"Loaded {len(plugins)} plugins")
68
+
69
+ # Add plugins to globals and __all__
70
+ for plugin_name, plugin in plugins.items():
71
+ globals()[plugin_name] = plugin
72
+ __all__.append(plugin_name)
73
+ logger.info(f"Registered plugin {plugin_name} in global namespace")
74
+
75
+ # Get exports from plugins and add them to globals
76
+ exports = get_exports()
77
+ logger.info(f"Found {len(exports)} exported objects from plugins")
78
+
79
+ for name, obj in exports.items():
80
+ globals()[name] = obj
81
+ __all__.append(name)
82
+ logger.info(f"Added plugin export: {name}")
83
+
84
+ # Add placeholders for expected exports that are missing
85
+ # This maintains backward compatibility for common plugins
86
+ PLUGIN_PLACEHOLDERS = {
87
+ # No placeholders - removed Conjure for cleaner namespace
88
+ }
89
+
90
+ for placeholder_name, github_url in PLUGIN_PLACEHOLDERS.items():
91
+ if placeholder_name not in globals():
92
+ # Create a placeholder class
93
+ placeholder_class = type(placeholder_name, (), {
94
+ "__getattr__": lambda self, name: self._not_installed(name),
95
+ "_not_installed": lambda self, name: self._raise_import_error(),
96
+ "_raise_import_error": lambda self: exec(f"""
97
+ msg = (
98
+ "The {placeholder_name} plugin is not installed. "
99
+ "To use {placeholder_name} with EDSL, install it using:\\n"
100
+ " from edsl.plugins import install_from_github\\n"
101
+ " install_from_github('{github_url}')\\n"
102
+ "\\nOr from the command line:\\n"
103
+ " edsl plugins install {github_url}"
104
+ )
105
+ logger.warning(msg)
106
+ raise ImportError(msg)
107
+ """)
108
+ })
109
+
110
+ # Register the placeholder
111
+ globals()[placeholder_name] = placeholder_class()
112
+ __all__.append(placeholder_name)
113
+ logger.info(f"Added placeholder for {placeholder_name} with installation instructions")
114
+
115
+ except ImportError as e:
116
+ # Modules not available
117
+ logger.info("Plugin system not available, skipping plugin loading: %s", e)
118
+ logger.debug("Plugin system not available, skipping plugin loading: %s", e)
119
+ except Exception as e:
120
+ # Error loading plugins
121
+ logger.error("Error loading plugins: %s", e)
122
+ logger.debug("Error loading plugins: %s", e)
123
+
124
+ # Now that all modules are loaded, configure logging from the config
125
+ logger.configure_from_config()
126
+
127
+
128
+ # Installs a custom exception handling routine for edsl exceptions
129
+ from .base.base_exception import BaseException
130
+ BaseException.install_exception_hook()
131
+
132
+ # Log the total number of items in __all__ for debugging
133
+ logger.debug(f"EDSL initialization complete with {len(__all__)} items in __all__")
11
134
 
12
- from .dataset import __all__ as dataset_all
13
- from .dataset import *
14
- __all__.extend(dataset_all)
15
-
16
- from .agents import __all__ as agents_all
17
- from .agents import *
18
- __all__.extend(agents_all)
19
-
20
- from .surveys import __all__ as surveys_all
21
- from .surveys import *
22
- __all__.extend(surveys_all)
23
-
24
- # Questions
25
- from .questions import __all__ as questions_all
26
- from .questions import *
27
- __all__.extend(questions_all)
28
-
29
- from .scenarios import __all__ as scenarios_all
30
- from .scenarios import *
31
- __all__.extend(scenarios_all)
32
-
33
- from .language_models import __all__ as language_models_all
34
- from .language_models import *
35
- __all__.extend(language_models_all)
36
-
37
- from .results import __all__ as results_all
38
- from .results import *
39
- __all__.extend(results_all)
40
-
41
- from .caching import __all__ as caching_all
42
- from .caching import *
43
- __all__.extend(caching_all)
44
-
45
- from .notebooks import __all__ as notebooks_all
46
- from .notebooks import *
47
- __all__.extend(notebooks_all)
48
-
49
- from .coop import __all__ as coop_all
50
- from .coop import *
51
- __all__.extend(coop_all)
52
-
53
- from .instructions import __all__ as instructions_all
54
- from .instructions import *
55
- __all__.extend(instructions_all)
56
-
57
- from .jobs import __all__ as jobs_all
58
- from .jobs import *
59
- __all__.extend(jobs_all)
60
-
61
- from .study import __all__ as study_all
62
- from .study import *
63
- __all__.extend(study_all)
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.49"
1
+ __version__ = "0.1.51"
edsl/agents/agent.py CHANGED
@@ -47,7 +47,6 @@ import types
47
47
  import warnings
48
48
  from uuid import uuid4
49
49
  from contextlib import contextmanager
50
-
51
50
  from typing import (
52
51
  Callable,
53
52
  Optional,
@@ -57,11 +56,9 @@ from typing import (
57
56
  Protocol,
58
57
  runtime_checkable,
59
58
  TypeVar,
59
+ Type,
60
60
  )
61
61
 
62
- # Type variable for the Agent class
63
- A = TypeVar("A", bound="Agent")
64
-
65
62
  if TYPE_CHECKING:
66
63
  from ..caching import Cache
67
64
  from ..surveys import Survey
@@ -71,17 +68,8 @@ if TYPE_CHECKING:
71
68
  from ..questions import QuestionBase
72
69
  from ..invigilators import InvigilatorBase
73
70
  from ..prompts import Prompt
74
- from ..questions import QuestionBase
75
71
  from ..key_management import KeyLookup
76
72
 
77
-
78
- @runtime_checkable
79
- class DirectAnswerMethod(Protocol):
80
- """Protocol defining the required signature for direct answer methods."""
81
-
82
- def __call__(self, self_: A, question: QuestionBase, scenario: Scenario) -> Any: ...
83
-
84
-
85
73
  from ..base import Base
86
74
  from ..scenarios import Scenario
87
75
  from ..questions import QuestionScenarioRenderError
@@ -93,7 +81,6 @@ from ..utilities import (
93
81
  remove_edsl_version,
94
82
  )
95
83
 
96
-
97
84
  from .exceptions import (
98
85
  AgentErrors,
99
86
  AgentCombinationError,
@@ -108,6 +95,16 @@ from .descriptors import (
108
95
  NameDescriptor,
109
96
  )
110
97
 
98
+ # Type variable for the Agent class
99
+ A = TypeVar("A", bound="Agent")
100
+
101
+
102
+ @runtime_checkable
103
+ class DirectAnswerMethod(Protocol):
104
+ """Protocol defining the required signature for direct answer methods."""
105
+
106
+ def __call__(self, self_: A, question: QuestionBase, scenario: Scenario) -> Any: ...
107
+
111
108
 
112
109
  class AgentTraits(Scenario):
113
110
  """A class representing the traits of an agent.
@@ -472,9 +469,10 @@ class Agent(Base):
472
469
  # Transfer direct answering method if present
473
470
  if hasattr(self, "answer_question_directly"):
474
471
  answer_question_directly = self.answer_question_directly
475
- newf = lambda self, question, scenario: answer_question_directly(
476
- question, scenario
477
- )
472
+ def newf(self, question, scenario):
473
+ return answer_question_directly(
474
+ question, scenario
475
+ )
478
476
  new_agent.add_direct_question_answering_method(newf)
479
477
 
480
478
  # Transfer dynamic traits function if present
@@ -940,7 +938,7 @@ class Agent(Base):
940
938
 
941
939
  answer_question = sync_wrapper(async_answer_question)
942
940
 
943
- def _get_invigilator_class(self, question: QuestionBase) -> Type[InvigilatorBase]:
941
+ def _get_invigilator_class(self, question: "QuestionBase") -> Type["InvigilatorBase"]:
944
942
  """Get the invigilator class for a question.
945
943
 
946
944
  This method returns the invigilator class that should be used to answer a question.
@@ -1098,6 +1096,8 @@ class Agent(Base):
1098
1096
  if name in self._traits:
1099
1097
  return self._traits[name]
1100
1098
 
1099
+ # Keep using AttributeError instead of our custom exception to maintain compatibility
1100
+ # with Python's attribute access mechanism
1101
1101
  raise AttributeError(
1102
1102
  f"'{type(self).__name__}' object has no attribute '{name}'"
1103
1103
  )
@@ -1142,7 +1142,7 @@ class Agent(Base):
1142
1142
  raw_data.pop("instruction")
1143
1143
  if self.codebook == {}:
1144
1144
  raw_data.pop("codebook")
1145
- if self.name == None:
1145
+ if self.name is None:
1146
1146
  raw_data.pop("name")
1147
1147
 
1148
1148
  if hasattr(self, "dynamic_traits_function"):
@@ -1164,7 +1164,7 @@ class Agent(Base):
1164
1164
 
1165
1165
  if (
1166
1166
  answer_question_directly_func
1167
- and raw_data.get("answer_question_directly_source_code", None) != None
1167
+ and raw_data.get("answer_question_directly_source_code", None) is not None
1168
1168
  ):
1169
1169
  raw_data["answer_question_directly_source_code"] = inspect.getsource(
1170
1170
  answer_question_directly_func
@@ -1352,7 +1352,7 @@ def main():
1352
1352
  question_name="food_preference",
1353
1353
  )
1354
1354
  job = question.by(agent)
1355
- results = job.run()
1355
+ job.run() # results not used
1356
1356
 
1357
1357
 
1358
1358
  if __name__ == "__main__":
edsl/agents/agent_list.py CHANGED
@@ -27,10 +27,7 @@ from .exceptions import AgentListError
27
27
  logger = logging.getLogger(__name__)
28
28
 
29
29
  if TYPE_CHECKING:
30
- from ..scenarios import ScenarioList
31
30
  from ..agents import Agent
32
- from pandas import DataFrame
33
- from ..dataset import Dataset
34
31
 
35
32
 
36
33
  def is_iterable(obj):
@@ -212,7 +209,7 @@ class AgentList(UserList, Base, AgentListOperationsMixin):
212
209
  new_data = [
213
210
  agent for agent in self.data if create_evaluator(agent).eval(expression)
214
211
  ]
215
- except NameNotDefined as e:
212
+ except NameNotDefined:
216
213
  e = AgentListError(f"'{expression}' is not a valid expression.")
217
214
  if is_notebook():
218
215
  print(e, file=sys.stderr)
@@ -451,7 +448,7 @@ class AgentList(UserList, Base, AgentListOperationsMixin):
451
448
  *fields,
452
449
  tablefmt: Optional[str] = None,
453
450
  pretty_labels: Optional[dict] = None,
454
- ) -> "Table":
451
+ ) -> Any:
455
452
  if len(self) == 0:
456
453
  e = AgentListError("Cannot create a table from an empty AgentList.")
457
454
  if is_notebook():
edsl/agents/exceptions.py CHANGED
@@ -1,18 +1,52 @@
1
- import sys
2
1
 
3
- from ..exceptions.BaseException import BaseException
4
- from ..utilities.is_notebook import is_notebook
2
+ from ..base.base_exception import BaseException
5
3
 
6
4
 
7
5
  class AgentListError(BaseException):
6
+ """
7
+ Exception raised when an AgentList operation fails.
8
+
9
+ This exception is raised in the following cases:
10
+ - When an invalid expression is provided in the filter() method
11
+ - When trying to add traits with mismatched lengths
12
+ - When attempting to create a table from an empty AgentList
13
+
14
+ Examples:
15
+ ```python
16
+ agents.filter("invalid expression") # Raises AgentListError
17
+ agents.add_trait(name="scores", values=[1, 2]) # Raises AgentListError if agents list has different length
18
+ ```
19
+ """
8
20
  relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#agent-lists"
9
21
 
10
22
 
11
23
  class AgentErrors(BaseException):
24
+ """
25
+ Base exception class for all agent-related errors.
26
+
27
+ This class is the parent of all agent-specific exceptions and may also be raised directly
28
+ when modifying agent traits or during operations like renaming or adding traits.
29
+ """
12
30
  relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html"
13
31
 
14
32
 
15
33
  class AgentDynamicTraitsFunctionError(AgentErrors):
34
+ """
35
+ Exception raised when there's an issue with the dynamic traits function.
36
+
37
+ This exception occurs when:
38
+ - The dynamic traits function has too many parameters
39
+ - The dynamic traits function has parameters other than 'question'
40
+
41
+ This error typically indicates that your dynamic traits function has an incorrect signature.
42
+ The function should accept only one parameter named 'question'.
43
+
44
+ Examples:
45
+ ```python
46
+ def wrong_func(question, extra_param): # Will raise AgentDynamicTraitsFunctionError
47
+ return {"trait": "value"}
48
+ ```
49
+ """
16
50
  relevant_doc = (
17
51
  "https://docs.expectedparrot.com/en/latest/agents.html#dynamic-traits-function"
18
52
  )
@@ -20,26 +54,106 @@ class AgentDynamicTraitsFunctionError(AgentErrors):
20
54
 
21
55
 
22
56
  class AgentDirectAnswerFunctionError(AgentErrors):
57
+ """
58
+ Exception raised when there's an issue with the direct answer method.
59
+
60
+ This exception occurs when the direct answer method doesn't have the required parameters.
61
+ The method must include 'question', 'scenario', and/or 'self' parameters.
62
+
63
+ Examples:
64
+ ```python
65
+ def wrong_answer_func(wrong_param): # Will raise AgentDirectAnswerFunctionError
66
+ return "Answer"
67
+ ```
68
+ """
23
69
  relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#agent-direct-answering-methods"
24
70
 
25
71
 
26
72
  class AgentCombinationError(AgentErrors):
73
+ """
74
+ Exception raised when attempting to combine agents with overlapping traits.
75
+
76
+ This exception occurs when you try to combine agents that have the same trait names,
77
+ which would result in ambiguous trait values in the combined agent.
78
+
79
+ To fix this, ensure that the agents being combined have unique trait names,
80
+ or rename the conflicting traits before combination.
81
+
82
+ Examples:
83
+ ```python
84
+ agent1 = Agent(name="A1", age=30)
85
+ agent2 = Agent(name="A2", age=40)
86
+ agent1 + agent2 # Raises AgentCombinationError due to duplicate 'age' trait
87
+ ```
88
+ """
27
89
  relevant_doc = (
28
90
  "https://docs.expectedparrot.com/en/latest/agents.html#combining-agents"
29
91
  )
30
92
 
31
93
 
32
94
  class AgentNameError(AgentErrors):
95
+ """
96
+ Exception raised when there's an issue with an agent's name.
97
+
98
+ This exception occurs when a trait key conflicts with the 'name' parameter,
99
+ as 'name' is a special attribute for agents and cannot be used as a trait name.
100
+
101
+ Examples:
102
+ ```python
103
+ Agent(name="John", name="John") # Raises AgentNameError
104
+ agent.add_trait(name="name", value="NewName") # Raises AgentNameError
105
+ ```
106
+ """
33
107
  relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#agent-names"
34
108
 
35
109
 
36
110
  class AgentTraitKeyError(AgentErrors):
111
+ """
112
+ Exception raised when an invalid trait key is used.
113
+
114
+ This exception occurs when a trait key is not a valid Python identifier.
115
+ Trait keys must follow Python variable naming rules (no spaces, no special characters
116
+ except underscore, cannot start with a number).
117
+
118
+ Examples:
119
+ ```python
120
+ Agent(name="John", "invalid-key"=30) # Raises AgentTraitKeyError
121
+ agent.add_trait(name="2invalid", value="value") # Raises AgentTraitKeyError
122
+ ```
123
+ """
37
124
  relevant_doc = (
38
125
  "https://docs.expectedparrot.com/en/latest/agents.html#constructing-an-agent"
39
126
  )
40
127
 
41
128
 
42
- class FailedTaskException(Exception):
43
- def __init__(self, message, agent_response_dict):
129
+ class AgentAttributeError(AgentErrors):
130
+ """
131
+ Exception raised when accessing a non-existent attribute of an agent.
132
+
133
+ This exception occurs when trying to access a trait or attribute that
134
+ doesn't exist on the agent.
135
+
136
+ Examples:
137
+ ```python
138
+ agent = Agent(name="John", age=30)
139
+ agent.height # Raises AgentAttributeError as 'height' doesn't exist
140
+ ```
141
+ """
142
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#agent-traits"
143
+
144
+ def __init__(self, message):
44
145
  super().__init__(message)
146
+
147
+
148
+ class FailedTaskException(BaseException):
149
+ """
150
+ Exception raised when an agent task execution fails.
151
+
152
+ This exception is used to track agent execution failures and retain information
153
+ about the agent's response when the failure occurred.
154
+
155
+ Note: This exception class is currently not used in the codebase.
156
+ """
157
+ def __init__(self, message, agent_response_dict):
158
+ super().__init__(f"Agent task failed: {message}")
45
159
  self.agent_response_dict = agent_response_dict
edsl/base/__init__.py CHANGED
@@ -7,7 +7,6 @@ from edsl.base.base_class import (
7
7
  Base,
8
8
  BaseDiff,
9
9
  BaseDiffCollection,
10
- BaseException,
11
10
  DiffMethodsMixin,
12
11
  DisplayJSON,
13
12
  DisplayYAML,
@@ -18,6 +17,15 @@ from edsl.base.base_class import (
18
17
  RepresentationMixin,
19
18
  is_iterable,
20
19
  )
20
+ from edsl.base.base_exception import BaseException
21
+ from edsl.base.exceptions import (
22
+ BaseValueError,
23
+ BaseNotImplementedError,
24
+ BaseKeyError,
25
+ BaseFileError,
26
+ BaseTypeError
27
+ )
28
+
21
29
  from edsl.base.enums import (
22
30
  EnumWithChecks,
23
31
  InferenceServiceLiteral,
@@ -39,37 +47,4 @@ from edsl.base.data_transfer_models import (
39
47
  ModelResponse,
40
48
  )
41
49
 
42
- __all__ = [
43
- # From base_class
44
- "Base",
45
- "BaseDiff",
46
- "BaseDiffCollection",
47
- "BaseException",
48
- "DiffMethodsMixin",
49
- "DisplayJSON",
50
- "DisplayYAML",
51
- "DummyObject",
52
- "HashingMixin",
53
- "PersistenceMixin",
54
- "RegisterSubclassesMeta",
55
- "RepresentationMixin",
56
- "is_iterable",
57
- # From enums
58
- "EnumWithChecks",
59
- "InferenceServiceLiteral",
60
- "InferenceServiceType",
61
- "QuestionType",
62
- "TokenPricing",
63
- "available_models_urls",
64
- "get_token_pricing",
65
- "pricing",
66
- "service_to_api_keyname",
67
- # From data_transfer_models
68
- "AgentResponseDict",
69
- "Answers",
70
- "EDSLOutput",
71
- "EDSLResultObjectInput",
72
- "ImageInfo",
73
- "ModelInputs",
74
- "ModelResponse",
75
- ]
50
+ __all__ = []