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
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
2
  from typing import Optional
3
- from ..descriptors import QuestionOptionsDescriptor
4
- from ..question_multiple_choice import QuestionMultipleChoice
5
-
6
- from ..decorators import inject_exception
3
+ from .descriptors import QuestionOptionsDescriptor
4
+ from .question_multiple_choice import QuestionMultipleChoice
5
+ from .decorators import inject_exception
7
6
 
8
7
 
9
8
  class QuestionYesNo(QuestionMultipleChoice):
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
  from abc import ABCMeta
3
3
  import inspect
4
4
 
5
- from ..enums import QuestionType
6
5
  from .exceptions import QuestionMissingTypeError, QuestionBadTypeError
7
6
 
8
7
  class RegisterQuestionsMeta(ABCMeta):
@@ -63,7 +62,8 @@ class RegisterQuestionsMeta(ABCMeta):
63
62
  if hasattr(cls, "question_type"):
64
63
  d[cls.question_type] = cls
65
64
  else:
66
- raise Exception(
65
+ from .exceptions import QuestionMissingTypeError
66
+ raise QuestionMissingTypeError(
67
67
  f"Class {classname} does not have a question_type class attribute"
68
68
  )
69
69
  return d
@@ -1,7 +1,7 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Optional, Any, List, TypedDict, TYPE_CHECKING
1
+ from abc import ABC
2
+ from typing import Optional, List, TYPE_CHECKING
3
3
 
4
- from pydantic import BaseModel, Field, field_validator, ValidationError
4
+ from pydantic import BaseModel, ValidationError
5
5
 
6
6
  from .exceptions import QuestionAnswerValidationError
7
7
  from .ExceptionExplainer import ExceptionExplainer
@@ -22,7 +22,8 @@ class ResponseValidatorABC(ABC):
22
22
  required_class_vars = ["required_params", "valid_examples", "invalid_examples"]
23
23
  for var in required_class_vars:
24
24
  if not hasattr(cls, var):
25
- raise ValueError(f"Class {cls.__name__} must have a '{var}' attribute.")
25
+ from .exceptions import QuestionValueError
26
+ raise QuestionValueError(f"Class {cls.__name__} must have a '{var}' attribute.")
26
27
 
27
28
  def __init__(
28
29
  self,
@@ -41,7 +42,8 @@ class ResponseValidatorABC(ABC):
41
42
  param for param in self.required_params if param not in kwargs
42
43
  ]
43
44
  if missing_params:
44
- raise ValueError(
45
+ from .exceptions import QuestionValueError
46
+ raise QuestionValueError(
45
47
  f"Missing required parameters: {', '.join(missing_params)}"
46
48
  )
47
49
 
@@ -75,8 +77,12 @@ class ResponseValidatorABC(ABC):
75
77
  ConstrainedNumericResponse(answer=42, comment=None, generated_tokens=None)
76
78
  """
77
79
  try:
78
- return self.response_model(**data)
79
- except ValidationError as e:
80
+ # Use model_validate instead of direct instantiation
81
+ return self.response_model.model_validate(data)
82
+ except (ValidationError, QuestionAnswerValidationError) as e:
83
+ # Pass through QuestionAnswerValidationError or convert ValidationError
84
+ if isinstance(e, QuestionAnswerValidationError):
85
+ raise
80
86
  raise QuestionAnswerValidationError(
81
87
  message=str(e), pydantic_error=e, data=data, model=self.response_model
82
88
  )
@@ -98,20 +104,12 @@ class ResponseValidatorABC(ABC):
98
104
  {'answer': 42, 'comment': None, 'generated_tokens': None}
99
105
  >>> rv.max_value
100
106
  86.7
101
- >>> rv.validate({"answer": "120"})
102
- Traceback (most recent call last):
103
- ...
104
- edsl.questions.exceptions.QuestionAnswerValidationError:...
105
107
  >>> from edsl import QuestionNumerical
106
108
  >>> q = QuestionNumerical.example()
107
109
  >>> q.permissive = True
108
110
  >>> rv = q.response_validator
109
111
  >>> rv.validate({"answer": "120"})
110
112
  {'answer': 120, 'comment': None, 'generated_tokens': None}
111
- >>> rv.validate({"answer": "poo"})
112
- Traceback (most recent call last):
113
- ...
114
- edsl.questions.exceptions.QuestionAnswerValidationError:...
115
113
  """
116
114
  proposed_edsl_answer_dict = self._preprocess(raw_edsl_answer_dict)
117
115
  try:
@@ -1,5 +1,6 @@
1
- from edsl.questions.data_structures import BaseModel
2
- from edsl.questions.response_validator_abc import ResponseValidatorABC
1
+ from typing import Type, List
2
+ from .data_structures import BaseModel
3
+ from .response_validator_abc import ResponseValidatorABC
3
4
 
4
5
 
5
6
  class ResponseValidatorFactory:
@@ -9,7 +10,7 @@ class ResponseValidatorFactory:
9
10
  self.question = question
10
11
 
11
12
  @property
12
- def response_model(self) -> type["BaseModel"]:
13
+ def response_model(self) -> Type["BaseModel"]:
13
14
  if self.question._response_model is not None:
14
15
  return self.question._response_model
15
16
  else:
@@ -18,17 +19,14 @@ class ResponseValidatorFactory:
18
19
  @property
19
20
  def response_validator(self) -> "ResponseValidatorABC":
20
21
  """Return the response validator."""
21
- params = (
22
- {
23
- "response_model": self.question.response_model,
24
- }
25
- | {k: getattr(self.question, k) for k in self.validator_parameters}
26
- | {"exception_to_throw": getattr(self.question, "exception_to_throw", None)}
27
- | {"override_answer": getattr(self.question, "override_answer", None)}
28
- )
22
+ params = {}
23
+ params.update({"response_model": self.question.response_model})
24
+ params.update({k: getattr(self.question, k) for k in self.validator_parameters})
25
+ params.update({"exception_to_throw": getattr(self.question, "exception_to_throw", None)})
26
+ params.update({"override_answer": getattr(self.question, "override_answer", None)})
29
27
  return self.question.response_validator_class(**params)
30
28
 
31
29
  @property
32
- def validator_parameters(self) -> list[str]:
30
+ def validator_parameters(self) -> List[str]:
33
31
  """Return the parameters required for the response validator."""
34
32
  return self.question.response_validator_class.required_params
@@ -1,4 +1,5 @@
1
1
  Please respond with a dictionary using the following keys: {{ answer_keys | join(', ') }}.
2
+ Do not include "python" for create a code block. Just return the dictionary.
2
3
 
3
4
  {% if value_descriptions %}
4
5
  Here are descriptions of the values to provide:
@@ -11,5 +11,5 @@ The options are:
11
11
  {% endfor %}
12
12
  {% endif %}
13
13
  {% if num_selections %}
14
- You can inlcude up to {{num_selections}} options in your answer.
14
+ You have to include {{num_selections}} options in your answer.
15
15
  {% endif %}
edsl/results/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
  from .results import Results
3
3
  from .result import Result
4
4
 
5
- __all__ = ["Results"]
5
+ __all__ = ["Results", "Result"]
@@ -2,28 +2,162 @@
2
2
  from ..base import BaseException
3
3
 
4
4
  class ResultsError(BaseException):
5
- relevant_docs = "https://docs.expectedparrot.com/en/latest/results.html"
5
+ """
6
+ Base exception class for all results-related errors.
7
+
8
+ This is the parent class for all exceptions related to Results objects
9
+ operations, including data manipulation, selection, and filtering.
10
+
11
+ This exception is raised in the following cases:
12
+ - When trying to add two Results objects with different surveys or created columns
13
+ - When trying to sample more items than available
14
+ - When Survey is not defined when accessing answer_keys
15
+ - When fetching remote Results fails
16
+ - When inappropriate model types are used with Results methods
17
+ """
18
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html"
6
19
 
7
20
 
8
21
  class ResultsDeserializationError(ResultsError):
9
- pass
22
+ """
23
+ Exception raised when Results object deserialization fails.
24
+
25
+ This exception occurs when a Results object cannot be properly reconstructed
26
+ from its serialized representation, typically during from_dict() operations.
27
+
28
+ Reasons this might occur:
29
+ - Missing required fields in the serialized data
30
+ - Corrupted serialized data
31
+ - Version incompatibility between serialized data and current code
32
+
33
+ To fix this error:
34
+ 1. Check that the serialized data is complete and uncorrupted
35
+ 2. Ensure you're using a compatible version of EDSL to deserialize the data
36
+ 3. If the issue persists, you may need to recreate the results from raw data
37
+
38
+ Examples:
39
+ ```python
40
+ Results.from_dict(incomplete_or_corrupted_data) # Raises ResultsDeserializationError
41
+ ```
42
+ """
43
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#saving-and-loading-results"
10
44
 
11
45
 
12
46
  class ResultsBadMutationstringError(ResultsError):
13
- pass
47
+ """
48
+ Exception raised when an invalid mutation string is provided.
49
+
50
+ This exception occurs when the mutation string doesn't follow the required format,
51
+ which should be 'column_name = expression' where expression is a valid Python
52
+ expression that can reference other columns.
53
+
54
+ To fix this error:
55
+ 1. Ensure your mutation string contains an equals sign
56
+ 2. Check that the left side is a valid column name
57
+ 3. Verify the right side is a valid Python expression
58
+
59
+ Examples:
60
+ ```python
61
+ results.mutate("invalid_mutation_no_equals") # Raises ResultsBadMutationstringError
62
+ results.mutate("column_name == value") # Raises ResultsBadMutationstringError (should use single =)
63
+ ```
64
+ """
65
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#creating-new-columns"
14
66
 
15
67
 
16
68
  class ResultsColumnNotFoundError(ResultsError):
17
- pass
69
+ """
70
+ Exception raised when attempting to access a non-existent column.
71
+
72
+ This exception occurs when trying to access, filter, or perform operations
73
+ on a column that doesn't exist in the Results object.
74
+
75
+ To fix this error:
76
+ 1. Check for typos in the column name
77
+ 2. Verify the column exists using results.columns() or results.df.columns
78
+ 3. If the column is dynamic, ensure it has been created with mutate() first
79
+
80
+ The error message typically includes suggestions for similar column names
81
+ that do exist, which can help identify typos.
82
+
83
+ Examples:
84
+ ```python
85
+ results.table(keys=["non_existent_column"]) # Raises ResultsColumnNotFoundError
86
+ results.select("typo_in_column_name") # Raises ResultsColumnNotFoundError
87
+ ```
88
+ """
89
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#selecting-columns"
18
90
 
19
91
 
20
92
  class ResultsInvalidNameError(ResultsError):
21
- pass
93
+ """
94
+ Exception raised when an invalid column name is provided.
95
+
96
+ This exception occurs when:
97
+ - The provided name is not a valid Python identifier
98
+ - The name conflicts with reserved names or methods
99
+ - The name contains invalid characters or starts with a number
100
+
101
+ To fix this error:
102
+ 1. Use names that follow Python variable naming rules
103
+ 2. Avoid using reserved words or existing method names
104
+ 3. Use only letters, numbers, and underscores (not starting with a number)
105
+
106
+ Examples:
107
+ ```python
108
+ results.mutate("123invalid = 1") # Raises ResultsInvalidNameError (starts with number)
109
+ results.mutate("invalid-name = 1") # Raises ResultsInvalidNameError (contains hyphen)
110
+ results.mutate("filter = 1") # Raises ResultsInvalidNameError (reserved method name)
111
+ ```
112
+ """
113
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#creating-new-columns"
22
114
 
23
115
 
24
116
  class ResultsMutateError(ResultsError):
25
- pass
117
+ """
118
+ Exception raised when a mutation operation fails.
119
+
120
+ This exception occurs when an error happens during the execution of a mutation
121
+ expression, such as:
122
+ - Syntax errors in the expression
123
+ - Reference to non-existent columns
124
+ - Type errors in operations (e.g., adding a string to a number)
125
+
126
+ To fix this error:
127
+ 1. Check the expression syntax
128
+ 2. Verify all columns referenced in the expression exist
129
+ 3. Ensure type compatibility in operations
130
+ 4. Test the expression with simple cases first
131
+
132
+ Examples:
133
+ ```python
134
+ results.mutate("new_col = old_col + 'text'") # Raises ResultsMutateError if old_col contains numbers
135
+ results.mutate("new_col = undefined_col + 1") # Raises ResultsMutateError if undefined_col doesn't exist
136
+ ```
137
+ """
138
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#creating-new-columns"
26
139
 
27
140
 
28
141
  class ResultsFilterError(ResultsError):
29
- pass
142
+ """
143
+ Exception raised when a filter operation fails.
144
+
145
+ This exception occurs when there's an error in the filter expression, such as:
146
+ - Using single equals (=) instead of double equals (==) for comparison
147
+ - Syntax errors in the filter expression
148
+ - Reference to non-existent columns
149
+ - Type errors in comparisons
150
+
151
+ To fix this error:
152
+ 1. Use == (double equals) for equality comparisons, not = (single equals)
153
+ 2. Check the filter expression syntax
154
+ 3. Verify all columns referenced in the expression exist
155
+ 4. Ensure type compatibility in comparisons
156
+
157
+ Examples:
158
+ ```python
159
+ results.filter("column = value") # Raises ResultsFilterError (use == instead)
160
+ results.filter("column == undefined_var") # Raises ResultsFilterError if undefined_var isn't defined
161
+ ```
162
+ """
163
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/results.html#filtering-results"
edsl/results/report.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import jinja2
2
2
  import textwrap
3
- import warnings
4
3
 
5
4
  class Report:
6
5
  """
@@ -174,7 +173,7 @@ class Report:
174
173
 
175
174
  if __name__ == "__main__":
176
175
  # Suppose you have an existing Dataset
177
- from edsl import Results
176
+ from .. import Results
178
177
  ds = Results.example().select("how_feeling", "how_feeling_yesterday")
179
178
 
180
179
  # Provide a custom template string
edsl/results/result.py CHANGED
@@ -23,7 +23,7 @@ maintaining a rich object model.
23
23
  from __future__ import annotations
24
24
  import inspect
25
25
  from collections import UserDict
26
- from typing import Any, Type, Callable, Optional, TYPE_CHECKING, Union
26
+ from typing import Any, Callable, Optional, TYPE_CHECKING, Union
27
27
 
28
28
  from ..base import Base
29
29
  from ..utilities import remove_edsl_version
@@ -35,7 +35,6 @@ if TYPE_CHECKING:
35
35
  from ..agents import Agent
36
36
  from ..scenarios import Scenario
37
37
  from ..language_models import LanguageModel
38
- from ..prompts import Prompt
39
38
  from ..surveys import Survey
40
39
 
41
40
  QuestionName = str
@@ -259,15 +258,17 @@ class Result(Base, UserDict):
259
258
 
260
259
  def check_expression(self, expression: str) -> None:
261
260
  for key in self.problem_keys:
262
- if key in expression and not key + "." in expression:
263
- raise ValueError(
264
- f"Key by iself {key} is problematic. Use the full key {key + '.' + key} name instead."
261
+ if key in expression and key + "." not in expression:
262
+ from .exceptions import ResultsColumnNotFoundError
263
+ raise ResultsColumnNotFoundError(
264
+ f"Key by itself {key} is problematic. Use the full key {key + '.' + key} name instead."
265
265
  )
266
266
  return None
267
267
 
268
268
  def code(self):
269
269
  """Return a string of code that can be used to recreate the Result object."""
270
- raise NotImplementedError
270
+ from .exceptions import ResultsError
271
+ raise ResultsError("The code() method is not implemented for Result objects")
271
272
 
272
273
  @property
273
274
  def problem_keys(self) -> list[str]:
@@ -307,7 +308,7 @@ class Result(Base, UserDict):
307
308
  return self._combined_dict
308
309
 
309
310
  @property
310
- def problem_keys(self) -> list[str]:
311
+ def get_problem_keys(self) -> list[str]:
311
312
  """Return a list of keys that are problematic."""
312
313
  if self._combined_dict is None or self._problem_keys is None:
313
314
  self._compute_combined_dict_and_problem_keys()
@@ -568,7 +569,8 @@ class Result(Base, UserDict):
568
569
  elif v.default is not v.empty:
569
570
  params[k] = v.default
570
571
  else:
571
- raise ValueError(f"Parameter {k} not found in Result object")
572
+ from .exceptions import ResultsError
573
+ raise ResultsError(f"Parameter {k} not found in Result object")
572
574
  return scoring_function(**params)
573
575
 
574
576
  @classmethod
@@ -579,7 +581,7 @@ class Result(Base, UserDict):
579
581
 
580
582
  def get_question_results(
581
583
  model_response_objects,
582
- ) -> dict[str, "EDSLResultObjectInput"]:
584
+ ) -> dict[str, Any]:
583
585
  """Maps the question name to the EDSLResultObjectInput."""
584
586
  question_results = {}
585
587
  for result in model_response_objects: