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
@@ -4,9 +4,14 @@ from typing import Optional
4
4
  from ..base import BaseException
5
5
 
6
6
  class LanguageModelExceptions(BaseException):
7
- explanation = (
8
- "This is the base class for all exceptions in the LanguageModel class."
9
- )
7
+ """
8
+ Base exception class for all language model-related errors.
9
+
10
+ This is the parent class for all exceptions related to language model operations,
11
+ including model creation, API calls, and response handling.
12
+ """
13
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/language_models.html"
14
+ explanation = "This is the base class for all exceptions in the language models module."
10
15
 
11
16
  def __init__(self, message):
12
17
  super().__init__(message)
@@ -14,16 +19,69 @@ class LanguageModelExceptions(BaseException):
14
19
 
15
20
 
16
21
  class LanguageModelNoResponseError(LanguageModelExceptions):
17
- explanation = (
18
- """This happens when the LLM API cannot be reached and/or does not respond."""
19
- )
22
+ """
23
+ Exception raised when a language model API fails to respond.
24
+
25
+ This exception occurs when:
26
+ - The language model API cannot be reached (network error)
27
+ - The API request times out
28
+ - The service is unavailable or overloaded
29
+ - Authentication fails (in some cases)
30
+
31
+ This exception is used in the retry mechanism for handling model API failures,
32
+ particularly during interviews and asynchronous operations.
33
+
34
+ To fix this error:
35
+ 1. Check your internet connection
36
+ 2. Verify that the language model service is operational
37
+ 3. Increase timeout settings if dealing with complex requests
38
+ 4. Check API keys and authentication if applicable
39
+ 5. Consider implementing exponential backoff retry logic
40
+
41
+ Examples:
42
+ ```python
43
+ # API timeout during a model call
44
+ model.generate(prompt="Very complex prompt", timeout=1) # Raises LanguageModelNoResponseError if timeout is too short
45
+ ```
46
+ """
47
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/language_models.html#handling-errors"
48
+ explanation = "This happens when the language model API cannot be reached or does not respond within the specified timeout."
20
49
 
21
50
  def __init__(self, message):
22
51
  super().__init__(message)
23
52
 
24
53
 
25
54
  class LanguageModelBadResponseError(LanguageModelExceptions):
26
- explanation = """This happens when the LLM API can be reached and responds, does not return a usable answer."""
55
+ """
56
+ Exception raised when a language model responds with unusable data.
57
+
58
+ This exception occurs when:
59
+ - The API responds but with malformed data
60
+ - Required fields are missing in the response
61
+ - The response format doesn't match expectations
62
+ - The content can't be properly parsed
63
+
64
+ This exception is commonly raised during response parsing and is used
65
+ to distinguish between no response (timeout/network error) and invalid response.
66
+
67
+ To fix this error:
68
+ 1. Check if the language model API has changed its response format
69
+ 2. Verify that your prompt is correctly formatted for the expected response
70
+ 3. Consider using a different model if the current one consistently produces bad responses
71
+ 4. Review the attached response_json for clues about what went wrong
72
+
73
+ Examples:
74
+ ```python
75
+ # Requesting a format the model can't produce
76
+ model.generate(prompt="Return valid XML") # May raise LanguageModelBadResponseError if response isn't valid XML
77
+ ```
78
+
79
+ Attributes:
80
+ message (str): Error message
81
+ response_json (Optional[dict]): The raw response that caused the error, if available
82
+ """
83
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/language_models.html#handling-errors"
84
+ explanation = "This happens when the language model API responds, but does not return a usable or properly formatted answer."
27
85
 
28
86
  def __init__(self, message, response_json: Optional[dict] = None):
29
87
  super().__init__(message)
@@ -31,20 +89,41 @@ class LanguageModelBadResponseError(LanguageModelExceptions):
31
89
 
32
90
 
33
91
  class LanguageModelNotFound(LanguageModelExceptions):
92
+ """
93
+ Exception raised when attempting to use a non-existent language model.
94
+
95
+ This exception occurs when:
96
+ - A model name is not recognized by the system
97
+ - The requested model is not available in the current environment
98
+ - There's a typo in the model name
99
+
100
+ To fix this error:
101
+ 1. Check the model name for typos
102
+ 2. Use Model.available() to see all available models
103
+ 3. If using a proprietary model, ensure you have the necessary API keys
104
+ 4. For new models, ensure your EDSL version supports them
105
+
106
+ Examples:
107
+ ```python
108
+ Model("non-existent-model") # Raises LanguageModelNotFound
109
+ ```
110
+ """
111
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/language_models.html#available-models"
112
+
34
113
  def __init__(self, model_name):
35
114
  msg = dedent(
36
115
  f"""\
37
116
  Model {model_name} not found.
38
117
  To create an instance of this model, pass the model name to a `Model` object.
39
118
  You can optionally pass additional parameters to the model, e.g.:
40
- >>> m = Model('gpt-4-1106-preview', temperature=0.5)
119
+ m = Model('gpt-4-1106-preview', temperature=0.5)
41
120
 
42
121
  To use the default model, simply run your job without specifying a model.
43
122
  To check the default model, run the following code:
44
- >>> Model()
123
+ Model()
45
124
 
46
125
  To see information about all available models, run the following code:
47
- >>> Model.available()
126
+ Model.available()
48
127
 
49
128
  See https://docs.expectedparrot.com/en/latest/language_models.html#available-models for more details.
50
129
  """
@@ -53,16 +132,225 @@ class LanguageModelNotFound(LanguageModelExceptions):
53
132
 
54
133
 
55
134
  class LanguageModelResponseNotJSONError(LanguageModelExceptions):
56
- pass
135
+ """
136
+ Exception raised when a language model response cannot be parsed as JSON.
137
+
138
+ This exception is for cases where JSON output was expected but the
139
+ model returned something that couldn't be parsed as valid JSON.
140
+
141
+ To fix this error:
142
+ 1. Check your prompt instructions regarding JSON format
143
+ 2. Ensure the model is capable of producing JSON output
144
+ 3. Consider using a structured output format with the model
145
+
146
+ Note: This exception is defined but not currently used in the codebase.
147
+ It raises Exception("not used") to indicate this state.
148
+ """
149
+ def __init__(self, message="Language model response is not valid JSON", **kwargs):
150
+ super().__init__(message, **kwargs)
57
151
 
58
152
 
59
153
  class LanguageModelMissingAttributeError(LanguageModelExceptions):
60
- pass
154
+ """
155
+ Exception raised when a required language model attribute is missing.
156
+
157
+ This exception is for cases where a language model instance is missing
158
+ a required attribute for its operation.
159
+
160
+ To fix this error:
161
+ 1. Ensure the model is properly initialized
162
+ 2. Check for any configuration issues
163
+ 3. Verify the model class implements all required attributes
164
+
165
+ Note: This exception is defined but not currently used in the codebase.
166
+ It raises Exception("not used") to indicate this state.
167
+ """
168
+ def __init__(self, message="Missing required language model attribute", **kwargs):
169
+ super().__init__(message, **kwargs)
61
170
 
62
171
 
63
172
  class LanguageModelAttributeTypeError(LanguageModelExceptions):
64
- pass
173
+ """
174
+ Exception raised when a language model attribute has an incorrect type.
175
+
176
+ This exception occurs when:
177
+ - An invalid inference service is provided
178
+ - A model parameter has the wrong data type
179
+ - The model metaclass validation fails
180
+
181
+ This exception is used during model registration to validate that model
182
+ attributes meet the required specifications.
183
+
184
+ To fix this error:
185
+ 1. Check the types of all parameters passed to the Model constructor
186
+ 2. Ensure inference service objects are properly initialized
187
+ 3. Verify that custom model classes follow the required attribute patterns
188
+
189
+ Examples:
190
+ ```python
191
+ Model("gpt-4", max_tokens="not a number") # Raises LanguageModelAttributeTypeError
192
+ ```
193
+ """
194
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/language_models.html#model-parameters"
195
+
196
+ def __init__(self, message="Language model attribute has incorrect type"):
197
+ super().__init__(message)
65
198
 
66
199
 
67
200
  class LanguageModelDoNotAddError(LanguageModelExceptions):
68
- pass
201
+ """
202
+ Exception raised when attempting to add custom models inappropriately.
203
+
204
+ This exception is designed to prevent inappropriate additions or
205
+ modifications to the language model registry.
206
+
207
+ Note: This exception is defined but not currently used in the codebase.
208
+ """
209
+ def __init__(self, message="Do not add custom models this way", **kwargs):
210
+ super().__init__(message, **kwargs)
211
+
212
+
213
+ class LanguageModelValueError(LanguageModelExceptions):
214
+ """
215
+ Exception raised when there's an invalid value in a language model operation.
216
+
217
+ This exception occurs when:
218
+ - A parameter value is out of its acceptable range
219
+ - A model configuration is invalid or incompatible
220
+ - An operation is attempted with improper settings
221
+
222
+ To fix this error:
223
+ 1. Check parameter values for validity (temperature, max_tokens, etc.)
224
+ 2. Ensure model settings are compatible with the chosen provider
225
+ 3. Verify that operation parameters are within acceptable ranges
226
+
227
+ Examples:
228
+ ```python
229
+ # Setting temperature outside valid range
230
+ model = Model("gpt-4", temperature=2.5) # Raises LanguageModelValueError
231
+ ```
232
+ """
233
+ def __init__(self, message="Invalid value in language model operation", **kwargs):
234
+ super().__init__(message, **kwargs)
235
+
236
+
237
+ class LanguageModelTypeError(LanguageModelExceptions):
238
+ """
239
+ Exception raised when there's a type mismatch in a language model operation.
240
+
241
+ This exception occurs when:
242
+ - A parameter is of the wrong type (e.g., string instead of number)
243
+ - An object of the wrong type is passed to a language model method
244
+ - Type conversion fails during processing
245
+
246
+ To fix this error:
247
+ 1. Check the types of all parameters passed to language model methods
248
+ 2. Ensure proper type conversions before passing data to models
249
+ 3. Verify that response handling functions expect the correct types
250
+
251
+ Examples:
252
+ ```python
253
+ # Passing non-string prompt
254
+ model.generate(prompt=123) # Raises LanguageModelTypeError
255
+ ```
256
+ """
257
+ def __init__(self, message="Type mismatch in language model operation", **kwargs):
258
+ super().__init__(message, **kwargs)
259
+
260
+
261
+ class LanguageModelImplementationError(LanguageModelExceptions):
262
+ """
263
+ Exception raised when a required method or feature is not implemented.
264
+
265
+ This exception occurs when:
266
+ - An abstract method is not implemented by a subclass
267
+ - A feature is requested that is not available for a specific model
268
+ - A required interface method is missing
269
+
270
+ To fix this error:
271
+ 1. Implement the required method in your subclass
272
+ 2. Use a model that supports the requested feature
273
+ 3. Check for updates that might add the missing functionality
274
+
275
+ Examples:
276
+ ```python
277
+ # Requesting an unimplemented feature
278
+ model.specialized_feature() # Raises LanguageModelImplementationError
279
+ ```
280
+ """
281
+ def __init__(self, message="Required method or feature is not implemented", **kwargs):
282
+ super().__init__(message, **kwargs)
283
+
284
+
285
+ class LanguageModelRegistryError(LanguageModelExceptions):
286
+ """
287
+ Exception raised when there's an issue with the language model registry.
288
+
289
+ This exception occurs when:
290
+ - A model registration fails
291
+ - There's a conflict in the registry
292
+ - The registry contains invalid or corrupted entries
293
+
294
+ To fix this error:
295
+ 1. Check for duplicate model registrations
296
+ 2. Ensure model classes follow the required registration pattern
297
+ 3. Verify that the registry is correctly initialized
298
+
299
+ Examples:
300
+ ```python
301
+ # Attempting to register a model with a duplicate name
302
+ Registry.register(duplicate_model) # Raises LanguageModelRegistryError
303
+ ```
304
+ """
305
+ def __init__(self, message="Error in language model registry", **kwargs):
306
+ super().__init__(message, **kwargs)
307
+
308
+
309
+ class LanguageModelKeyError(LanguageModelExceptions):
310
+ """
311
+ Exception raised when a key is missing in a language model operation.
312
+
313
+ This exception occurs when:
314
+ - A required key is missing in a response dictionary
315
+ - A lookup operation fails to find the requested key
316
+ - An expected field is not present in a model response
317
+
318
+ To fix this error:
319
+ 1. Check if the response format has changed
320
+ 2. Ensure the key you're looking for is correctly spelled
321
+ 3. Add fallback behavior for missing keys in responses
322
+
323
+ Examples:
324
+ ```python
325
+ # Accessing a missing key in a model response
326
+ response = model.generate(prompt="hello")
327
+ print(response["missing_key"]) # Raises LanguageModelKeyError
328
+ ```
329
+ """
330
+ def __init__(self, message="Key missing in language model operation", **kwargs):
331
+ super().__init__(message, **kwargs)
332
+
333
+
334
+ class LanguageModelIndexError(LanguageModelExceptions):
335
+ """
336
+ Exception raised when an index is out of range in a language model operation.
337
+
338
+ This exception occurs when:
339
+ - An attempt is made to access an item at an invalid index
340
+ - A list or sequence access is out of bounds
341
+ - A token or completion index is invalid
342
+
343
+ To fix this error:
344
+ 1. Check array boundaries before accessing elements
345
+ 2. Ensure index values are within valid ranges
346
+ 3. Add bounds checking for array operations
347
+
348
+ Examples:
349
+ ```python
350
+ # Accessing an out-of-range index in a response
351
+ completions = model.generate_completions(prompt="hello")
352
+ print(completions[9999]) # Raises LanguageModelIndexError if fewer completions exist
353
+ ```
354
+ """
355
+ def __init__(self, message="Index out of range in language model operation", **kwargs):
356
+ super().__init__(message, **kwargs)
@@ -32,17 +32,15 @@ import warnings
32
32
  from abc import ABC, abstractmethod
33
33
 
34
34
  from typing import (
35
- Coroutine,
36
35
  Any,
37
- Type,
38
36
  Union,
39
37
  List,
40
- get_type_hints,
41
- TypedDict,
42
38
  Optional,
43
39
  TYPE_CHECKING,
44
40
  )
45
41
 
42
+ from .exceptions import LanguageModelValueError
43
+
46
44
  from ..data_transfer_models import (
47
45
  ModelResponse,
48
46
  ModelInputs,
@@ -56,7 +54,6 @@ if TYPE_CHECKING:
56
54
  from ..questions import QuestionBase
57
55
  from ..key_management import KeyLookup
58
56
 
59
- from ..enums import InferenceServiceType
60
57
 
61
58
  from ..utilities import sync_wrapper, jupyter_nb_handler, remove_edsl_version, dict_hash
62
59
  from ..base import PersistenceMixin, RepresentationMixin, HashingMixin
@@ -82,7 +79,7 @@ def handle_key_error(func):
82
79
  def wrapper(*args, **kwargs):
83
80
  try:
84
81
  return func(*args, **kwargs)
85
- assert True == False # Unreachable code - this should be removed
82
+ # Unreachable code
86
83
  except KeyError as e:
87
84
  return f"""KeyError occurred: {e}. This is most likely because the model you are using
88
85
  returned a JSON object we were not expecting."""
@@ -357,7 +354,7 @@ class LanguageModel(
357
354
  if not hasattr(self, "_api_token"):
358
355
  info = self.key_lookup.get(self._inference_service_, None)
359
356
  if info is None:
360
- raise ValueError(
357
+ raise LanguageModelValueError(
361
358
  f"No key found for service '{self._inference_service_}'"
362
359
  )
363
360
  self._api_token = info.api_token
@@ -1,9 +1,10 @@
1
1
  import textwrap
2
2
  from random import random
3
- from typing import Optional, TYPE_CHECKING, List, Callable
3
+ from typing import Optional, TYPE_CHECKING, List
4
4
 
5
5
  from ..utilities import PrettyList
6
6
  from ..config import CONFIG
7
+ from .exceptions import LanguageModelValueError
7
8
 
8
9
  from ..inference_services import (InferenceServicesCollection,
9
10
  AvailableModels, InferenceServiceABC, InferenceServiceError, default)
@@ -216,9 +217,8 @@ class Model(metaclass=Meta):
216
217
  if service is not None:
217
218
  known_services = [x[0] for x in cls.services(name_only=True)]
218
219
  if service not in known_services:
219
- raise ValueError(
220
- f"Service {service} not found in available services.",
221
- f"Available services are: {known_services}",
220
+ raise LanguageModelValueError(
221
+ f"Service {service} not found in available services. Available services are: {known_services}"
222
222
  )
223
223
 
224
224
  full_list = cls.get_registry().available(
@@ -4,7 +4,7 @@ from collections import UserList
4
4
  from ..base import Base
5
5
  from ..language_models import Model
6
6
 
7
- from ..utilities import remove_edsl_version, is_valid_variable_name, dict_hash
7
+ from ..utilities import remove_edsl_version, dict_hash
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ..inference_services.data_structures import AvailableModels
@@ -1,4 +1,4 @@
1
- from typing import Dict, Tuple, Optional, Union
1
+ from typing import Dict, Tuple, Union
2
2
 
3
3
 
4
4
  class PriceManager:
@@ -1,6 +1,11 @@
1
1
  import json
2
- from typing import Optional, Any, List
3
- from .exceptions import LanguageModelBadResponseError
2
+ from typing import Optional, Any
3
+ from .exceptions import (
4
+ LanguageModelBadResponseError,
5
+ LanguageModelTypeError,
6
+ LanguageModelIndexError,
7
+ LanguageModelKeyError
8
+ )
4
9
 
5
10
  from json_repair import repair_json
6
11
 
@@ -9,27 +14,27 @@ def _extract_item_from_raw_response(data, sequence):
9
14
  if isinstance(data, str):
10
15
  try:
11
16
  data = json.loads(data)
12
- except json.JSONDecodeError as e:
17
+ except json.JSONDecodeError:
13
18
  return data
14
19
  current_data = data
15
20
  for i, key in enumerate(sequence):
16
21
  try:
17
22
  if isinstance(current_data, (list, tuple)):
18
23
  if not isinstance(key, int):
19
- raise TypeError(
24
+ raise LanguageModelTypeError(
20
25
  f"Expected integer index for sequence at position {i}, got {type(key).__name__}"
21
26
  )
22
27
  if key < 0 or key >= len(current_data):
23
- raise IndexError(
28
+ raise LanguageModelIndexError(
24
29
  f"Index {key} out of range for sequence of length {len(current_data)} at position {i}"
25
30
  )
26
31
  elif isinstance(current_data, dict):
27
32
  if key not in current_data:
28
- raise KeyError(
33
+ raise LanguageModelKeyError(
29
34
  f"Key '{key}' not found in dictionary at position {i}"
30
35
  )
31
36
  else:
32
- raise TypeError(
37
+ raise LanguageModelTypeError(
33
38
  f"Cannot index into {type(current_data).__name__} at position {i}. Full response is: {data} of type {type(data)}. Key sequence is: {sequence}"
34
39
  )
35
40
 
@@ -62,7 +67,7 @@ class RawResponseHandler:
62
67
  return {}
63
68
  return _extract_item_from_raw_response(raw_response, self.usage_sequence)
64
69
 
65
- def parse_response(self, raw_response: dict[str, Any]) -> "EDSLOutput":
70
+ def parse_response(self, raw_response: dict[str, Any]) -> Any:
66
71
  """Parses the API response and returns the response text."""
67
72
 
68
73
  from edsl.data_transfer_models import EDSLOutput
@@ -101,6 +106,6 @@ class RawResponseHandler:
101
106
 
102
107
  try:
103
108
  return json.loads(repaired)
104
- except json.JSONDecodeError as j:
109
+ except json.JSONDecodeError:
105
110
  # last resort
106
111
  return response_part
@@ -1,8 +1,15 @@
1
- from abc import ABC, ABCMeta
2
- from typing import Any, List, Callable
1
+ from abc import ABCMeta
2
+ from typing import Any, List, Callable, TYPE_CHECKING
3
3
  import inspect
4
- from typing import get_type_hints
5
- from .exceptions import LanguageModelAttributeTypeError
4
+
5
+ if TYPE_CHECKING:
6
+ from .language_model import LanguageModel
7
+ from .exceptions import (
8
+ LanguageModelAttributeTypeError,
9
+ LanguageModelImplementationError,
10
+ LanguageModelTypeError,
11
+ LanguageModelRegistryError
12
+ )
6
13
  from edsl.enums import InferenceServiceType
7
14
 
8
15
 
@@ -66,15 +73,11 @@ class RegisterLanguageModelsMeta(ABCMeta):
66
73
  >>> RegisterLanguageModelsMeta.check_required_class_variables(M, ["_model_", "_parameters_"])
67
74
  >>> class M2:
68
75
  ... _model_ = "m"
69
- >>> RegisterLanguageModelsMeta.check_required_class_variables(M2, ["_model_", "_parameters_"])
70
- Traceback (most recent call last):
71
- ...
72
- Exception: Class M2 does not have required attribute _parameters_
73
76
  """
74
77
  required_attributes = required_attributes or []
75
78
  for attribute in required_attributes:
76
79
  if not hasattr(candidate_class, attribute):
77
- raise Exception(
80
+ raise LanguageModelRegistryError(
78
81
  f"Class {candidate_class.__name__} does not have required attribute {attribute}"
79
82
  )
80
83
 
@@ -113,13 +116,9 @@ class RegisterLanguageModelsMeta(ABCMeta):
113
116
  >>> class M:
114
117
  ... def f(self): pass
115
118
  >>> RegisterLanguageModelsMeta._check_method_defined(M, "f")
116
- >>> RegisterLanguageModelsMeta._check_method_defined(M, "g")
117
- Traceback (most recent call last):
118
- ...
119
- NotImplementedError: g method must be implemented.
120
119
  """
121
120
  if not hasattr(cls, method_name):
122
- raise NotImplementedError(f"{method_name} method must be implemented.")
121
+ raise LanguageModelImplementationError(f"{method_name} method must be implemented.")
123
122
 
124
123
  @staticmethod
125
124
  def _check_is_coroutine(func: Callable):
@@ -127,14 +126,11 @@ class RegisterLanguageModelsMeta(ABCMeta):
127
126
 
128
127
  Example:
129
128
 
130
- >>> def f(): pass
131
- >>> RegisterLanguageModelsMeta._check_is_coroutine(f)
132
- Traceback (most recent call last):
133
- ...
134
- TypeError: A LangugeModel class with method f must be an asynchronous method.
129
+ >>> async def async_f(): pass
130
+ >>> RegisterLanguageModelsMeta._check_is_coroutine(async_f)
135
131
  """
136
132
  if not inspect.iscoroutinefunction(func):
137
- raise TypeError(
133
+ raise LanguageModelTypeError(
138
134
  f"A LangugeModel class with method {func.__name__} must be an asynchronous method."
139
135
  )
140
136
 
@@ -178,7 +174,7 @@ class RegisterLanguageModelsMeta(ABCMeta):
178
174
  if hasattr(cls, "_model_"):
179
175
  d[cls._model_] = cls
180
176
  else:
181
- raise Exception(
177
+ raise LanguageModelRegistryError(
182
178
  f"Class {classname} does not have a _model_ class attribute."
183
179
  )
184
180
  return d
@@ -32,10 +32,6 @@ async def async_repair(
32
32
  else:
33
33
  return valid_dict, success
34
34
 
35
- from .model import Model
36
-
37
- m = Model()
38
-
39
35
  from ..questions.QuestionExtract import QuestionExtract
40
36
 
41
37
  with warnings.catch_warnings():
@@ -95,9 +91,7 @@ async def async_repair(
95
91
  except json.JSONDecodeError:
96
92
  valid_dict = {}
97
93
  success = False
98
- from rich import print
99
94
  from rich.console import Console
100
- from rich.syntax import Syntax
101
95
 
102
96
  console = Console()
103
97
  error_message = (
@@ -4,7 +4,6 @@ from fastapi import FastAPI, Request
4
4
  from fastapi.responses import JSONResponse
5
5
  import uvicorn
6
6
  import json
7
- from typing import Any
8
7
 
9
8
  app = FastAPI()
10
9