edsl 0.1.48__py3-none-any.whl → 0.1.50__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 (239) 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 +75 -0
  14. edsl/buckets/model_buckets.py +1 -2
  15. edsl/buckets/token_bucket.py +11 -6
  16. edsl/buckets/token_bucket_api.py +1 -2
  17. edsl/buckets/token_bucket_client.py +9 -7
  18. edsl/caching/cache.py +7 -2
  19. edsl/caching/cache_entry.py +10 -9
  20. edsl/caching/exceptions.py +113 -7
  21. edsl/caching/remote_cache_sync.py +1 -2
  22. edsl/caching/sql_dict.py +17 -12
  23. edsl/cli.py +43 -0
  24. edsl/config/config_class.py +30 -6
  25. edsl/conversation/Conversation.py +3 -2
  26. edsl/conversation/exceptions.py +58 -0
  27. edsl/conversation/mug_negotiation.py +0 -2
  28. edsl/coop/__init__.py +20 -1
  29. edsl/coop/coop.py +129 -38
  30. edsl/coop/exceptions.py +188 -9
  31. edsl/coop/price_fetcher.py +3 -6
  32. edsl/coop/utils.py +4 -6
  33. edsl/dataset/__init__.py +5 -4
  34. edsl/dataset/dataset.py +53 -43
  35. edsl/dataset/dataset_operations_mixin.py +86 -72
  36. edsl/dataset/dataset_tree.py +9 -5
  37. edsl/dataset/display/table_display.py +0 -2
  38. edsl/dataset/display/table_renderers.py +0 -1
  39. edsl/dataset/exceptions.py +125 -0
  40. edsl/dataset/file_exports.py +18 -11
  41. edsl/dataset/r/ggplot.py +13 -6
  42. edsl/display/__init__.py +27 -0
  43. edsl/display/core.py +147 -0
  44. edsl/display/plugin.py +189 -0
  45. edsl/display/utils.py +52 -0
  46. edsl/inference_services/__init__.py +9 -1
  47. edsl/inference_services/available_model_cache_handler.py +1 -1
  48. edsl/inference_services/available_model_fetcher.py +4 -5
  49. edsl/inference_services/data_structures.py +9 -6
  50. edsl/inference_services/exceptions.py +132 -1
  51. edsl/inference_services/inference_service_abc.py +2 -2
  52. edsl/inference_services/inference_services_collection.py +2 -6
  53. edsl/inference_services/registry.py +4 -3
  54. edsl/inference_services/service_availability.py +2 -1
  55. edsl/inference_services/services/anthropic_service.py +4 -1
  56. edsl/inference_services/services/aws_bedrock.py +13 -12
  57. edsl/inference_services/services/azure_ai.py +12 -10
  58. edsl/inference_services/services/deep_infra_service.py +1 -4
  59. edsl/inference_services/services/deep_seek_service.py +1 -5
  60. edsl/inference_services/services/google_service.py +6 -2
  61. edsl/inference_services/services/groq_service.py +1 -1
  62. edsl/inference_services/services/mistral_ai_service.py +4 -2
  63. edsl/inference_services/services/ollama_service.py +1 -1
  64. edsl/inference_services/services/open_ai_service.py +7 -5
  65. edsl/inference_services/services/perplexity_service.py +6 -2
  66. edsl/inference_services/services/test_service.py +8 -7
  67. edsl/inference_services/services/together_ai_service.py +2 -3
  68. edsl/inference_services/services/xai_service.py +1 -1
  69. edsl/instructions/__init__.py +1 -1
  70. edsl/instructions/change_instruction.py +3 -2
  71. edsl/instructions/exceptions.py +61 -0
  72. edsl/instructions/instruction.py +5 -2
  73. edsl/instructions/instruction_collection.py +2 -1
  74. edsl/instructions/instruction_handler.py +4 -9
  75. edsl/interviews/ReportErrors.py +0 -3
  76. edsl/interviews/__init__.py +9 -2
  77. edsl/interviews/answering_function.py +11 -13
  78. edsl/interviews/exception_tracking.py +14 -7
  79. edsl/interviews/exceptions.py +79 -0
  80. edsl/interviews/interview.py +32 -29
  81. edsl/interviews/interview_status_dictionary.py +4 -2
  82. edsl/interviews/interview_status_log.py +2 -1
  83. edsl/interviews/interview_task_manager.py +3 -3
  84. edsl/interviews/request_token_estimator.py +3 -1
  85. edsl/interviews/statistics.py +2 -3
  86. edsl/invigilators/__init__.py +7 -1
  87. edsl/invigilators/exceptions.py +79 -0
  88. edsl/invigilators/invigilator_base.py +0 -1
  89. edsl/invigilators/invigilators.py +8 -12
  90. edsl/invigilators/prompt_constructor.py +1 -5
  91. edsl/invigilators/prompt_helpers.py +8 -4
  92. edsl/invigilators/question_instructions_prompt_builder.py +1 -1
  93. edsl/invigilators/question_option_processor.py +9 -5
  94. edsl/invigilators/question_template_replacements_builder.py +3 -2
  95. edsl/jobs/__init__.py +3 -3
  96. edsl/jobs/async_interview_runner.py +24 -22
  97. edsl/jobs/check_survey_scenario_compatibility.py +7 -6
  98. edsl/jobs/data_structures.py +7 -4
  99. edsl/jobs/exceptions.py +177 -8
  100. edsl/jobs/fetch_invigilator.py +1 -1
  101. edsl/jobs/jobs.py +72 -67
  102. edsl/jobs/jobs_checks.py +2 -3
  103. edsl/jobs/jobs_component_constructor.py +2 -2
  104. edsl/jobs/jobs_pricing_estimation.py +3 -2
  105. edsl/jobs/jobs_remote_inference_logger.py +5 -4
  106. edsl/jobs/jobs_runner_asyncio.py +1 -2
  107. edsl/jobs/jobs_runner_status.py +8 -9
  108. edsl/jobs/remote_inference.py +26 -23
  109. edsl/jobs/results_exceptions_handler.py +8 -5
  110. edsl/key_management/__init__.py +3 -1
  111. edsl/key_management/exceptions.py +62 -0
  112. edsl/key_management/key_lookup.py +1 -1
  113. edsl/key_management/key_lookup_builder.py +37 -14
  114. edsl/key_management/key_lookup_collection.py +2 -0
  115. edsl/language_models/__init__.py +1 -1
  116. edsl/language_models/exceptions.py +302 -14
  117. edsl/language_models/language_model.py +4 -7
  118. edsl/language_models/model.py +4 -4
  119. edsl/language_models/model_list.py +1 -1
  120. edsl/language_models/price_manager.py +1 -1
  121. edsl/language_models/raw_response_handler.py +14 -9
  122. edsl/language_models/registry.py +17 -21
  123. edsl/language_models/repair.py +0 -6
  124. edsl/language_models/unused/fake_openai_service.py +0 -1
  125. edsl/load_plugins.py +69 -0
  126. edsl/logger.py +146 -0
  127. edsl/notebooks/notebook.py +1 -1
  128. edsl/notebooks/notebook_to_latex.py +0 -1
  129. edsl/plugins/__init__.py +63 -0
  130. edsl/plugins/built_in/export_example.py +50 -0
  131. edsl/plugins/built_in/pig_latin.py +67 -0
  132. edsl/plugins/cli.py +372 -0
  133. edsl/plugins/cli_typer.py +283 -0
  134. edsl/plugins/exceptions.py +31 -0
  135. edsl/plugins/hookspec.py +51 -0
  136. edsl/plugins/plugin_host.py +128 -0
  137. edsl/plugins/plugin_manager.py +633 -0
  138. edsl/plugins/plugins_registry.py +168 -0
  139. edsl/prompts/__init__.py +2 -0
  140. edsl/prompts/exceptions.py +107 -5
  141. edsl/prompts/prompt.py +14 -6
  142. edsl/questions/HTMLQuestion.py +5 -11
  143. edsl/questions/Quick.py +0 -1
  144. edsl/questions/__init__.py +2 -0
  145. edsl/questions/answer_validator_mixin.py +318 -318
  146. edsl/questions/compose_questions.py +2 -2
  147. edsl/questions/descriptors.py +10 -49
  148. edsl/questions/exceptions.py +278 -22
  149. edsl/questions/loop_processor.py +7 -5
  150. edsl/questions/prompt_templates/question_list.jinja +3 -0
  151. edsl/questions/question_base.py +14 -16
  152. edsl/questions/question_base_gen_mixin.py +2 -2
  153. edsl/questions/question_base_prompts_mixin.py +9 -3
  154. edsl/questions/question_budget.py +9 -5
  155. edsl/questions/question_check_box.py +3 -5
  156. edsl/questions/question_dict.py +171 -194
  157. edsl/questions/question_extract.py +1 -1
  158. edsl/questions/question_free_text.py +4 -6
  159. edsl/questions/question_functional.py +4 -3
  160. edsl/questions/question_list.py +36 -9
  161. edsl/questions/question_matrix.py +95 -61
  162. edsl/questions/question_multiple_choice.py +6 -4
  163. edsl/questions/question_numerical.py +2 -4
  164. edsl/questions/question_registry.py +4 -2
  165. edsl/questions/register_questions_meta.py +0 -1
  166. edsl/questions/response_validator_abc.py +7 -13
  167. edsl/questions/templates/dict/answering_instructions.jinja +1 -0
  168. edsl/questions/templates/rank/question_presentation.jinja +1 -1
  169. edsl/results/__init__.py +1 -1
  170. edsl/results/exceptions.py +141 -7
  171. edsl/results/report.py +0 -1
  172. edsl/results/result.py +4 -5
  173. edsl/results/results.py +10 -51
  174. edsl/results/results_selector.py +8 -4
  175. edsl/scenarios/PdfExtractor.py +2 -2
  176. edsl/scenarios/construct_download_link.py +69 -35
  177. edsl/scenarios/directory_scanner.py +33 -14
  178. edsl/scenarios/document_chunker.py +1 -1
  179. edsl/scenarios/exceptions.py +238 -14
  180. edsl/scenarios/file_methods.py +1 -1
  181. edsl/scenarios/file_store.py +7 -3
  182. edsl/scenarios/handlers/__init__.py +17 -0
  183. edsl/scenarios/handlers/docx_file_store.py +0 -5
  184. edsl/scenarios/handlers/pdf_file_store.py +0 -1
  185. edsl/scenarios/handlers/pptx_file_store.py +0 -5
  186. edsl/scenarios/handlers/py_file_store.py +0 -1
  187. edsl/scenarios/handlers/sql_file_store.py +1 -4
  188. edsl/scenarios/handlers/sqlite_file_store.py +0 -1
  189. edsl/scenarios/handlers/txt_file_store.py +1 -1
  190. edsl/scenarios/scenario.py +0 -1
  191. edsl/scenarios/scenario_list.py +152 -18
  192. edsl/scenarios/scenario_list_pdf_tools.py +1 -0
  193. edsl/scenarios/scenario_selector.py +0 -1
  194. edsl/surveys/__init__.py +3 -4
  195. edsl/surveys/dag/__init__.py +4 -2
  196. edsl/surveys/descriptors.py +1 -1
  197. edsl/surveys/edit_survey.py +1 -0
  198. edsl/surveys/exceptions.py +165 -9
  199. edsl/surveys/memory/__init__.py +5 -3
  200. edsl/surveys/memory/memory_management.py +1 -0
  201. edsl/surveys/memory/memory_plan.py +6 -15
  202. edsl/surveys/rules/__init__.py +5 -3
  203. edsl/surveys/rules/rule.py +1 -2
  204. edsl/surveys/rules/rule_collection.py +1 -1
  205. edsl/surveys/survey.py +12 -24
  206. edsl/surveys/survey_export.py +6 -3
  207. edsl/surveys/survey_flow_visualization.py +10 -1
  208. edsl/tasks/__init__.py +2 -0
  209. edsl/tasks/question_task_creator.py +3 -3
  210. edsl/tasks/task_creators.py +1 -3
  211. edsl/tasks/task_history.py +5 -7
  212. edsl/tasks/task_status_log.py +1 -2
  213. edsl/tokens/__init__.py +3 -1
  214. edsl/tokens/token_usage.py +1 -1
  215. edsl/utilities/__init__.py +21 -1
  216. edsl/utilities/decorators.py +1 -2
  217. edsl/utilities/markdown_to_docx.py +2 -2
  218. edsl/utilities/markdown_to_pdf.py +1 -1
  219. edsl/utilities/repair_functions.py +0 -1
  220. edsl/utilities/restricted_python.py +0 -1
  221. edsl/utilities/template_loader.py +2 -3
  222. edsl/utilities/utilities.py +8 -29
  223. {edsl-0.1.48.dist-info → edsl-0.1.50.dist-info}/METADATA +32 -2
  224. edsl-0.1.50.dist-info/RECORD +363 -0
  225. edsl-0.1.50.dist-info/entry_points.txt +3 -0
  226. edsl/dataset/smart_objects.py +0 -96
  227. edsl/exceptions/BaseException.py +0 -21
  228. edsl/exceptions/__init__.py +0 -54
  229. edsl/exceptions/configuration.py +0 -16
  230. edsl/exceptions/general.py +0 -34
  231. edsl/study/ObjectEntry.py +0 -173
  232. edsl/study/ProofOfWork.py +0 -113
  233. edsl/study/SnapShot.py +0 -80
  234. edsl/study/Study.py +0 -520
  235. edsl/study/__init__.py +0 -6
  236. edsl/utilities/interface.py +0 -135
  237. edsl-0.1.48.dist-info/RECORD +0 -347
  238. {edsl-0.1.48.dist-info → edsl-0.1.50.dist-info}/LICENSE +0 -0
  239. {edsl-0.1.48.dist-info → edsl-0.1.50.dist-info}/WHEEL +0 -0
@@ -1,4 +1,3 @@
1
- from typing import TYPE_CHECKING
2
1
  from dataclasses import dataclass
3
2
 
4
3
 
@@ -46,12 +45,6 @@ class InstructionHandler:
46
45
  ['intro']
47
46
  >>> [i.name for i in s._relevant_instructions(q3)]
48
47
  []
49
-
50
- >>> i_change = ChangeInstruction(keep = ["poop"], drop = [])
51
- >>> s = Survey([q1, i, q2, i_change])
52
- Traceback (most recent call last):
53
- ...
54
- ValueError: ChangeInstruction change_instruction_0 references instruction poop which does not exist.
55
48
  """
56
49
  from .instruction import Instruction
57
50
  from .change_instruction import ChangeInstruction
@@ -70,7 +63,8 @@ class InstructionHandler:
70
63
  num_change_instructions += 1
71
64
  for prior_instruction in entry.keep + entry.drop:
72
65
  if prior_instruction not in instruction_names_to_instructions:
73
- raise ValueError(
66
+ from edsl.instructions.exceptions import InstructionValueError
67
+ raise InstructionValueError(
74
68
  f"ChangeInstruction {entry.name} references instruction {prior_instruction} which does not exist."
75
69
  )
76
70
  instructions_run_length += 1
@@ -83,7 +77,8 @@ class InstructionHandler:
83
77
  instructions_run_length = 0
84
78
  true_questions.append(entry)
85
79
  else:
86
- raise ValueError(
80
+ from edsl.instructions.exceptions import InstructionValueError
81
+ raise InstructionValueError(
87
82
  f"Entry {repr(entry)} is not a QuestionBase or an Instruction."
88
83
  )
89
84
 
@@ -1,7 +1,4 @@
1
- import json
2
- import requests
3
1
  import threading
4
- from ..coop import Coop
5
2
 
6
3
 
7
4
  class ReportErrors:
@@ -1,4 +1,11 @@
1
- from .interview_task_manager import InterviewTaskManager
1
+ from .interview import Interview
2
2
  from .interview_status_dictionary import InterviewStatusDictionary
3
3
  from .interview_status_log import InterviewStatusLog
4
- from .interview import Interview
4
+ from .interview_task_manager import InterviewTaskManager
5
+
6
+ __all__ = [
7
+ "InterviewTaskManager",
8
+ "InterviewStatusDictionary",
9
+ "InterviewStatusLog",
10
+ "Interview"
11
+ ]
@@ -1,21 +1,19 @@
1
- import copy
2
1
  import asyncio
3
-
4
- from typing import Union, Type, Callable, TYPE_CHECKING, Any
2
+ import copy
3
+ from typing import TYPE_CHECKING, Any, Callable, Union
5
4
 
6
5
  if TYPE_CHECKING:
6
+ from ..invigilators.invigilator_base import InvigilatorBase
7
+ from ..key_management import KeyLookup
7
8
  from ..questions import QuestionBase
8
9
  from .interview import Interview
9
- from ..key_management import KeyLookup
10
-
11
- from ..surveys.base import EndOfSurvey
12
- from ..tasks import TaskStatus
13
10
 
11
+ from ..data_transfer_models import EDSLResultObjectInput
14
12
  from ..jobs.fetch_invigilator import FetchInvigilator
15
13
  from ..language_models.exceptions import LanguageModelNoResponseError
16
14
  from ..questions.exceptions import QuestionAnswerValidationError
17
- from ..data_transfer_models import AgentResponseDict, EDSLResultObjectInput, Answers
18
-
15
+ from ..surveys.base import EndOfSurvey
16
+ from ..tasks import TaskStatus
19
17
  from .exception_tracking import InterviewExceptionEntry
20
18
 
21
19
 
@@ -46,7 +44,7 @@ class SkipHandler:
46
44
  | self.interview.agent["traits"]
47
45
  )
48
46
  return self.skip_function(current_question_index, combined_answers)
49
-
47
+
50
48
  def _current_info_env(self) -> dict[str, Any]:
51
49
  """
52
50
  - The current answers are "generated_tokens" and "comment"
@@ -112,7 +110,7 @@ class SkipHandler:
112
110
  if next_question_index > (current_question_index + 1):
113
111
  cancel_between(current_question_index + 1, next_question_index)
114
112
 
115
-
113
+
116
114
 
117
115
 
118
116
  class AnswerQuestionFunctionConstructor:
@@ -175,11 +173,11 @@ class AnswerQuestionFunctionConstructor:
175
173
  ) -> "EDSLResultObjectInput":
176
174
 
177
175
  from tenacity import (
176
+ RetryError,
178
177
  retry,
178
+ retry_if_exception_type,
179
179
  stop_after_attempt,
180
180
  wait_exponential,
181
- retry_if_exception_type,
182
- RetryError,
183
181
  )
184
182
 
185
183
  @retry(
@@ -1,10 +1,11 @@
1
- from collections import UserDict
2
- import traceback
3
1
  import datetime
4
2
  import json
3
+ import traceback
4
+ from collections import UserDict
5
5
 
6
6
  from ..invigilators import InvigilatorBase
7
7
 
8
+
8
9
  class InterviewExceptionEntry:
9
10
  """Class to record an exception that occurred during the interview."""
10
11
 
@@ -69,8 +70,8 @@ class InterviewExceptionEntry:
69
70
 
70
71
  >>> entry = InterviewExceptionEntry.example()
71
72
  """
72
- from ..questions import QuestionFreeText
73
73
  from ..language_models import LanguageModel
74
+ from ..questions import QuestionFreeText
74
75
 
75
76
  m = LanguageModel.example(test_model=True)
76
77
  q = QuestionFreeText.example(exception_to_throw=ValueError)
@@ -128,12 +129,11 @@ class InterviewExceptionEntry:
128
129
 
129
130
  @property
130
131
  def html_traceback(self) -> str:
132
+ from io import StringIO
133
+
131
134
  from rich.console import Console
132
- from rich.table import Table
133
135
  from rich.traceback import Traceback
134
136
 
135
- from io import StringIO
136
-
137
137
  html_output = StringIO()
138
138
 
139
139
  console = Console(file=html_output, record=True)
@@ -218,7 +218,6 @@ class InterviewExceptionEntry:
218
218
  return cls(exception=exception, invigilator=invigilator)
219
219
 
220
220
 
221
-
222
221
  class InterviewExceptionCollection(UserDict):
223
222
  """A collection of exceptions that occurred during the interview."""
224
223
 
@@ -231,6 +230,14 @@ class InterviewExceptionCollection(UserDict):
231
230
  """Return a list of unfixed exceptions."""
232
231
  return {k: v for k, v in self.data.items() if k not in self.fixed}
233
232
 
233
+ def num_exceptions(self) -> int:
234
+ """Return the total number of exceptions."""
235
+ return sum(len(v) for v in self.data.values())
236
+
237
+ def num_unfixed_exceptions(self) -> int:
238
+ """Return the number of unfixed exceptions."""
239
+ return sum(len(v) for v in self.unfixed_exceptions().values())
240
+
234
241
  def num_unfixed(self) -> int:
235
242
  """Return a list of unfixed questions."""
236
243
  return len([k for k in self.data.keys() if k not in self.fixed])
@@ -0,0 +1,79 @@
1
+ """
2
+ Exceptions specific to the interviews module.
3
+
4
+ This module defines custom exception classes for all interview-related errors
5
+ in the EDSL framework, ensuring consistent error handling and user feedback.
6
+ """
7
+
8
+ from ..base import BaseException
9
+
10
+
11
+ class InterviewError(BaseException):
12
+ """
13
+ Base exception class for all interview-related errors.
14
+
15
+ This is the parent class for all exceptions related to interview creation,
16
+ execution, and management in the EDSL framework.
17
+
18
+ Examples:
19
+ ```python
20
+ # Usually not raised directly, but through subclasses
21
+ # For example, when accessing incomplete tasks
22
+ interview.get_completed_task("incomplete_task") # Would raise InterviewTaskError
23
+ ```
24
+ """
25
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
26
+
27
+
28
+ class InterviewTaskError(InterviewError):
29
+ """
30
+ Exception raised when there's an issue with interview tasks.
31
+
32
+ This exception occurs when:
33
+ - Attempting to access tasks that are not complete
34
+ - Task execution fails due to errors in the model response
35
+ - Task dependencies are not satisfied
36
+
37
+ Examples:
38
+ ```python
39
+ # Attempting to access an incomplete task
40
+ interview.get_completed_task("incomplete_task") # Raises InterviewTaskError
41
+ ```
42
+ """
43
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
44
+
45
+
46
+ class InterviewStatusError(InterviewError):
47
+ """
48
+ Exception raised when there's an issue with interview status management.
49
+
50
+ This exception occurs when:
51
+ - Invalid operations are performed on interview status objects
52
+ - Incompatible status objects are combined
53
+ - Status tracking encounters inconsistent states
54
+
55
+ Examples:
56
+ ```python
57
+ # Attempting to add incompatible status objects
58
+ status_dict + incompatible_object # Raises InterviewStatusError
59
+ ```
60
+ """
61
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
62
+
63
+
64
+ class InterviewTokenError(InterviewError):
65
+ """
66
+ Exception raised when there's an issue with token estimation or usage.
67
+
68
+ This exception occurs when:
69
+ - Invalid inputs are provided to token estimators
70
+ - Token calculation fails due to unsupported prompt types
71
+ - Token limits are exceeded during an interview
72
+
73
+ Examples:
74
+ ```python
75
+ # Providing an invalid prompt type to token estimator
76
+ estimator.estimate_tokens(invalid_prompt) # Raises InterviewTokenError
77
+ ```
78
+ """
79
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/token_usage.html"
@@ -14,36 +14,38 @@ the individual API calls to language models, with support for caching and distri
14
14
  """
15
15
 
16
16
  from __future__ import annotations
17
+
17
18
  import asyncio
18
19
  import copy
19
20
  from dataclasses import dataclass
21
+ from typing import TYPE_CHECKING, Any, Generator, List, Optional, Type
20
22
 
21
- from typing import Any, Type, List, Generator, Optional, TYPE_CHECKING
23
+ if TYPE_CHECKING:
24
+ from ..jobs.data_structures import RunConfig
25
+ from .interview_status_log import InterviewStatusLog
22
26
 
23
- # from jobs module
24
- from ..jobs.data_structures import Answers
27
+ # from jobs module
25
28
  from ..buckets import ModelBuckets
29
+ from ..jobs.data_structures import Answers
26
30
  from ..jobs.fetch_invigilator import FetchInvigilator
27
- from ..utilities.utilities import dict_hash
28
31
  from ..surveys import Survey
32
+ from ..utilities.utilities import dict_hash
29
33
 
30
- # from interviews module
34
+ # from interviews module
31
35
  from .answering_function import AnswerQuestionFunctionConstructor
36
+ from .exception_tracking import InterviewExceptionCollection, InterviewExceptionEntry
37
+ from .interview_status_dictionary import InterviewStatusDictionary
32
38
  from .interview_task_manager import InterviewTaskManager
33
39
  from .request_token_estimator import RequestTokenEstimator
34
- from .interview_status_dictionary import InterviewStatusDictionary
35
- from .exception_tracking import InterviewExceptionCollection, InterviewExceptionEntry
36
-
37
40
 
38
41
  if TYPE_CHECKING:
39
42
  from ..agents import Agent
40
- from ..surveys import Survey
41
- from ..scenarios import Scenario
42
43
  from ..caching import Cache
44
+ from ..invigilators import InvigilatorBase
43
45
  from ..language_models import LanguageModel
46
+ from ..scenarios import Scenario
47
+ from ..surveys import Survey
44
48
  from ..tokens import InterviewTokenUsage
45
- from ..invigilators import InvigilatorBase
46
- from ..key_management import KeyLookup
47
49
 
48
50
 
49
51
  @dataclass
@@ -277,15 +279,15 @@ class Interview:
277
279
  "iteration": self.iteration,
278
280
  "exceptions": {},
279
281
  }
280
-
282
+
281
283
  # Optionally include exceptions
282
284
  if include_exceptions:
283
285
  d["exceptions"] = self.exceptions.to_dict()
284
-
286
+
285
287
  # Include custom indices if present
286
288
  if hasattr(self, "indices"):
287
289
  d["indices"] = self.indices
288
-
290
+
289
291
  return d
290
292
 
291
293
  @classmethod
@@ -304,9 +306,9 @@ class Interview:
304
306
  """
305
307
  # Import necessary classes
306
308
  from ..agents import Agent
307
- from ..surveys import Survey
308
- from ..scenarios import Scenario
309
309
  from ..language_models import LanguageModel
310
+ from ..scenarios import Scenario
311
+ from ..surveys import Survey
310
312
 
311
313
  # Deserialize each component
312
314
  agent = Agent.from_dict(d["agent"])
@@ -314,7 +316,7 @@ class Interview:
314
316
  scenario = Scenario.from_dict(d["scenario"])
315
317
  model = LanguageModel.from_dict(d["model"])
316
318
  iteration = d["iteration"]
317
-
319
+
318
320
  # Prepare constructor parameters
319
321
  params = {
320
322
  "agent": agent,
@@ -323,19 +325,19 @@ class Interview:
323
325
  "model": model,
324
326
  "iteration": iteration,
325
327
  }
326
-
328
+
327
329
  # Add optional indices if present
328
330
  if "indices" in d:
329
331
  params["indices"] = d["indices"]
330
-
332
+
331
333
  # Create the interview instance
332
334
  interview = cls(**params)
333
-
335
+
334
336
  # Restore exceptions if present
335
337
  if "exceptions" in d:
336
338
  exceptions = InterviewExceptionCollection.from_dict(d["exceptions"])
337
339
  interview.exceptions = exceptions
338
-
340
+
339
341
  return interview
340
342
 
341
343
  def __hash__(self) -> int:
@@ -416,7 +418,7 @@ class Interview:
416
418
  >>> run_config.parameters.stop_on_exception = True
417
419
  >>> result, _ = asyncio.run(i.async_conduct_interview(run_config))
418
420
  """
419
- from ..jobs import RunConfig, RunParameters, RunEnvironment
421
+ from ..jobs import RunConfig, RunEnvironment, RunParameters
420
422
 
421
423
  if run_config is None:
422
424
  run_config = RunConfig(
@@ -503,7 +505,7 @@ class Interview:
503
505
  result = invigilator.get_failed_task_result(
504
506
  failure_reason="Task was skipped."
505
507
  )
506
- except asyncio.CancelledError as e: # task was cancelled
508
+ except asyncio.CancelledError: # task was cancelled
507
509
  result = invigilator.get_failed_task_result(
508
510
  failure_reason="Task was cancelled."
509
511
  )
@@ -520,7 +522,8 @@ class Interview:
520
522
 
521
523
  for task, invigilator in zip(tasks, invigilators):
522
524
  if not task.done():
523
- raise ValueError(f"Task {task.get_name()} is not done.")
525
+ from edsl.interviews.exceptions import InterviewTaskError
526
+ raise InterviewTaskError(f"Task {task.get_name()} is not done.")
524
527
 
525
528
  yield handle_task(task, invigilator)
526
529
 
@@ -608,9 +611,9 @@ class Interview:
608
611
  True
609
612
  """
610
613
  from ..agents import Agent
611
- from ..surveys import Survey
612
- from ..scenarios import Scenario
613
614
  from ..language_models import LanguageModel
615
+ from ..scenarios import Scenario
616
+ from ..surveys import Survey
614
617
 
615
618
  # Define a simple direct answering method that always returns "yes"
616
619
  def f(self, question, scenario):
@@ -622,12 +625,12 @@ class Interview:
622
625
  survey = Survey.example()
623
626
  scenario = Scenario.example()
624
627
  model = LanguageModel.example()
625
-
628
+
626
629
  # If we want an interview that throws exceptions, configure it accordingly
627
630
  if throw_exception:
628
631
  model = LanguageModel.example(test_model=True, throw_exception=True)
629
632
  agent = Agent.example() # Without direct answering method
630
-
633
+
631
634
  # Create and return the interview
632
635
  return Interview(agent=agent, survey=survey, scenario=scenario, model=model)
633
636
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
+
2
3
  from collections import UserDict
3
- from typing import Union, Dict
4
+ from typing import Dict, Union
4
5
 
5
6
  from ..tasks.task_status_enum import TaskStatus, get_enum_from_string
6
7
 
@@ -26,7 +27,8 @@ class InterviewStatusDictionary(UserDict):
26
27
  ) -> "InterviewStatusDictionary":
27
28
  """Adds two InterviewStatusDictionaries together."""
28
29
  if not isinstance(other, InterviewStatusDictionary):
29
- raise ValueError(f"Can't add {type(other)} to InterviewStatusDictionary")
30
+ from edsl.interviews.exceptions import InterviewStatusError
31
+ raise InterviewStatusError(f"Can't add {type(other)} to InterviewStatusDictionary")
30
32
  new_dict = {}
31
33
  for key in self.keys():
32
34
  new_dict[key] = self[key] + other[key]
@@ -2,6 +2,7 @@ from collections import UserDict
2
2
 
3
3
  from ..tasks.task_status_enum import TaskStatus, status_colors
4
4
 
5
+
5
6
  class InterviewStatusLog(UserDict):
6
7
  """A dictionary of TaskStatusLog objects.
7
8
 
@@ -50,8 +51,8 @@ class InterviewStatusLog(UserDict):
50
51
  def visualize(self, num_periods: int = 10) -> None:
51
52
  """Visualize the status matrix with outlined squares."""
52
53
  import matplotlib.pyplot as plt
53
- from matplotlib.colors import ListedColormap
54
54
  import numpy as np
55
+ from matplotlib.colors import ListedColormap
55
56
  from matplotlib.patches import Rectangle
56
57
 
57
58
  # Define your custom colormap
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations
2
+
2
3
  import asyncio
3
- from typing import Any, Type, List, Generator, Optional, Union, TYPE_CHECKING
4
+ from typing import TYPE_CHECKING
4
5
 
5
6
  if TYPE_CHECKING:
6
7
  from ..questions import QuestionBase
7
8
  from ..tokens import InterviewTokenUsage
8
- from . import InterviewStatusDictionary
9
- from . import InterviewStatusLog
9
+ from . import InterviewStatusDictionary, InterviewStatusLog
10
10
 
11
11
 
12
12
  class InterviewTaskManager:
@@ -1,6 +1,7 @@
1
1
  from ..jobs.fetch_invigilator import FetchInvigilator
2
2
  from ..scenarios import FileStore
3
3
 
4
+
4
5
  class RequestTokenEstimator:
5
6
  """Estimate the number of tokens that will be required to run the focal task."""
6
7
 
@@ -25,7 +26,8 @@ class RequestTokenEstimator:
25
26
  if isinstance(file, FileStore):
26
27
  file_tokens += file.size * 0.25
27
28
  else:
28
- raise ValueError(f"Prompt is of type {type(prompt)}")
29
+ from edsl.interviews.exceptions import InterviewTokenError
30
+ raise InterviewTokenError(f"Prompt is of type {type(prompt)}")
29
31
  return len(combined_text) / 4.0 + file_tokens
30
32
 
31
33
 
@@ -1,6 +1,5 @@
1
1
  from collections import UserDict
2
- from typing import Literal, List, Type, DefaultDict
3
- from collections import UserDict
2
+ from typing import DefaultDict
4
3
 
5
4
  from ..tokens import InterviewTokenUsage
6
5
 
@@ -22,7 +21,7 @@ class InterviewStatistic(UserDict):
22
21
  >>> InterviewStatistic._format_number(1000, 1, "sec.")
23
22
  '1,000.0 sec.'
24
23
  """
25
- if type(number) == str:
24
+ if isinstance(number, str):
26
25
  return number
27
26
  else:
28
27
  return f"{number:,.{digits}f}" + " " + units
@@ -35,4 +35,10 @@ from .invigilator_base import InvigilatorBase
35
35
  from .invigilators import InvigilatorFunctional
36
36
  from .prompt_constructor import PromptConstructor
37
37
 
38
- __all_ = []
38
+ __all__ = [
39
+ 'InvigilatorAI',
40
+ 'InvigilatorHuman',
41
+ 'InvigilatorBase',
42
+ 'InvigilatorFunctional',
43
+ 'PromptConstructor'
44
+ ]
@@ -0,0 +1,79 @@
1
+ """
2
+ Exceptions specific to the invigilators module.
3
+
4
+ This module defines custom exception classes for all invigilator-related errors
5
+ in the EDSL framework, ensuring consistent error handling and user feedback.
6
+ """
7
+
8
+ from ..base import BaseException
9
+
10
+
11
+ class InvigilatorError(BaseException):
12
+ """
13
+ Base exception class for all invigilator-related errors.
14
+
15
+ This is the parent class for all exceptions related to invigilator creation,
16
+ execution, and management in the EDSL framework.
17
+
18
+ Examples:
19
+ ```python
20
+ # Usually not raised directly, but through subclasses
21
+ # For example, when attempting to use an unimplemented feature
22
+ invigilator.encode_image(image_path) # Would raise InvigilatorNotImplementedError
23
+ ```
24
+ """
25
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
26
+
27
+
28
+ class InvigilatorTypeError(InvigilatorError):
29
+ """
30
+ Exception raised when there's a type mismatch in invigilator operations.
31
+
32
+ This exception occurs when:
33
+ - Invalid input types are provided to invigilator methods
34
+ - Template variables have incompatible types
35
+ - Return value types don't match expected types
36
+
37
+ Examples:
38
+ ```python
39
+ # Providing an invalid type to an invigilator method
40
+ invigilator.process_options(123) # Raises InvigilatorTypeError if expected a dict
41
+ ```
42
+ """
43
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
44
+
45
+
46
+ class InvigilatorValueError(InvigilatorError):
47
+ """
48
+ Exception raised when invalid values are provided to invigilator operations.
49
+
50
+ This exception occurs when:
51
+ - Template variables are missing or invalid
52
+ - Option values don't match expected patterns
53
+ - Configuration values are outside allowed ranges
54
+
55
+ Examples:
56
+ ```python
57
+ # Template with invalid variables
58
+ invigilator.process_template("Hello {{ missing }}") # Raises InvigilatorValueError
59
+ ```
60
+ """
61
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
62
+
63
+
64
+ class InvigilatorNotImplementedError(InvigilatorError):
65
+ """
66
+ Exception raised when attempting to use an unimplemented feature.
67
+
68
+ This exception occurs when:
69
+ - Calling methods that are not implemented in a specific invigilator
70
+ - Using features only available in certain invigilator types
71
+ - Using planned functionality that is not yet available
72
+
73
+ Examples:
74
+ ```python
75
+ # Attempting to use image encoding when not implemented
76
+ invigilator.encode_image(image_path) # Raises InvigilatorNotImplementedError
77
+ ```
78
+ """
79
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
@@ -3,7 +3,6 @@ import asyncio
3
3
  from typing import Coroutine, Dict, Any, Optional, TYPE_CHECKING
4
4
 
5
5
  from ..utilities.decorators import jupyter_nb_handler
6
- from ..data_transfer_models import AgentResponseDict
7
6
 
8
7
  if TYPE_CHECKING:
9
8
  from ..prompts import Prompt
@@ -2,14 +2,12 @@
2
2
  from abc import ABC, abstractmethod
3
3
  import asyncio
4
4
  from typing import Coroutine, Dict, Any, Optional, TYPE_CHECKING
5
- from typing import Dict, Any, Optional, TYPE_CHECKING, Literal
5
+ from typing import Literal
6
6
 
7
7
  from ..utilities.decorators import sync_wrapper
8
8
  from ..questions.exceptions import QuestionAnswerValidationError
9
9
  from ..data_transfer_models import AgentResponseDict, EDSLResultObjectInput
10
10
  from ..utilities.decorators import jupyter_nb_handler
11
- from ..data_transfer_models import AgentResponseDict
12
- from ..data_transfer_models import EDSLResultObjectInput
13
11
 
14
12
  from .prompt_constructor import PromptConstructor
15
13
  from .prompt_helpers import PromptPlan
@@ -18,13 +16,10 @@ if TYPE_CHECKING:
18
16
  from ..prompts import Prompt
19
17
  from ..scenarios import Scenario
20
18
  from ..surveys import Survey
21
- from ..prompts import Prompt
22
19
  from ..caching import Cache
23
20
  from ..questions import QuestionBase
24
- from ..scenarios import Scenario
25
21
  from ..surveys.memory import MemoryPlan
26
22
  from ..language_models import LanguageModel
27
- from ..surveys import Survey
28
23
  from ..agents import Agent
29
24
  from ..key_management import KeyLookup
30
25
 
@@ -171,14 +166,14 @@ class InvigilatorBase(ABC):
171
166
  """
172
167
  data = {
173
168
  "answer": None,
174
- "generated_tokens": None,
169
+ "generated_tokens": getattr(self, "generated_tokens", None),
175
170
  "comment": failure_reason,
176
171
  "question_name": self.question.question_name,
177
172
  "prompts": self.get_prompts(),
178
- "cached_response": None,
179
- "raw_model_response": None,
180
- "cache_used": None,
181
- "cache_key": None,
173
+ "cached_response": getattr(self, "cached_response", None),
174
+ "raw_model_response": getattr(self, "raw_model_response", None),
175
+ "cache_used": getattr(self, "cache_used", None),
176
+ "cache_key": getattr(self, "cache_key", None),
182
177
  }
183
178
  return EDSLResultObjectInput(**data)
184
179
 
@@ -285,7 +280,8 @@ class InvigilatorAI(InvigilatorBase):
285
280
  }
286
281
  if "encoded_image" in prompts:
287
282
  params["encoded_image"] = prompts["encoded_image"]
288
- raise NotImplementedError("encoded_image not implemented")
283
+ from edsl.invigilators.exceptions import InvigilatorNotImplementedError
284
+ raise InvigilatorNotImplementedError("encoded_image not implemented")
289
285
 
290
286
  if "files_list" in prompts:
291
287
  params["files_list"] = prompts["files_list"]