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
@@ -0,0 +1,168 @@
1
+ """
2
+ Plugin registry module for accessing available plugins.
3
+
4
+ This module provides functionality to discover and retrieve information about
5
+ available plugins from the Expected Parrot cloud service. It defines data structures
6
+ for plugin metadata and provides methods to access the plugin registry.
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from typing import List, Optional, Dict, Any
11
+ import json
12
+ import random
13
+ from datetime import datetime
14
+
15
+ from ..base.base_exception import BaseException
16
+ from .exceptions import PluginException
17
+
18
+
19
+ @dataclass
20
+ class AvailablePlugin:
21
+ """
22
+ Data class representing an available plugin in the registry.
23
+
24
+ Attributes:
25
+ name: The name of the plugin
26
+ description: A description of the plugin's functionality
27
+ github_url: URL to the plugin's GitHub repository
28
+ version: Version of the plugin
29
+ author: Author of the plugin
30
+ tags: List of tags associated with the plugin
31
+ created_at: Date when the plugin was created
32
+ is_installed: Whether the plugin is currently installed locally
33
+ """
34
+ name: str
35
+ description: str
36
+ github_url: str
37
+ version: str = "0.1.0"
38
+ author: str = "Expected Parrot"
39
+ tags: List[str] = None
40
+ created_at: str = None
41
+ is_installed: bool = False
42
+
43
+ def __post_init__(self):
44
+ """Initialize default values for optional fields."""
45
+ if self.tags is None:
46
+ self.tags = []
47
+ if self.created_at is None:
48
+ self.created_at = datetime.now().strftime("%Y-%m-%d")
49
+
50
+
51
+ class PluginRegistryError(PluginException):
52
+ """
53
+ Exception raised when there's an issue with the plugin registry.
54
+
55
+ This exception is raised when the plugin registry cannot be accessed
56
+ or when there's an issue with the plugin data format.
57
+ """
58
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/plugins.html"
59
+
60
+
61
+ def get_available_plugins(refresh: bool = False) -> List[AvailablePlugin]:
62
+ """
63
+ Get a list of available plugins from the Expected Parrot.
64
+
65
+ This retrieves the official list of plugins available for EDSL.
66
+
67
+ Args:
68
+ refresh: Whether to refresh the cache and fetch the latest data
69
+
70
+ Returns:
71
+ List of AvailablePlugin objects representing available plugins
72
+
73
+ Raises:
74
+ PluginRegistryError: If the registry cannot be accessed or data is invalid
75
+ """
76
+ # Official plugins list
77
+ plugins = [
78
+ AvailablePlugin(
79
+ name="conjure",
80
+ description="Create EDSL objects from Qualtrics, SPSS, Stata, and other survey/statistical files",
81
+ github_url="https://github.com/expectedparrot/edsl-conjure",
82
+ version="1.0.0",
83
+ author="Expected Parrot",
84
+ tags=["qualtrics", "stata", "spss", "survey", "web", "import", "convert"],
85
+ )
86
+ ]
87
+
88
+ return plugins
89
+
90
+
91
+ def search_plugins(query: str, tags: Optional[List[str]] = None) -> List[AvailablePlugin]:
92
+ """
93
+ Search for plugins matching a query string or tags.
94
+
95
+ Args:
96
+ query: Search query string
97
+ tags: Optional list of tags to filter by
98
+
99
+ Returns:
100
+ List of matching AvailablePlugin objects
101
+ """
102
+ all_plugins = get_available_plugins()
103
+
104
+ # Filter by search query
105
+ if query:
106
+ query = query.lower()
107
+ filtered_plugins = [
108
+ plugin for plugin in all_plugins
109
+ if query in plugin.name.lower() or query in plugin.description.lower()
110
+ ]
111
+ else:
112
+ filtered_plugins = all_plugins
113
+
114
+ # Filter by tags if provided
115
+ if tags:
116
+ tags = [tag.lower() for tag in tags]
117
+ filtered_plugins = [
118
+ plugin for plugin in filtered_plugins
119
+ if any(tag in [t.lower() for t in plugin.tags] for tag in tags)
120
+ ]
121
+
122
+ return filtered_plugins
123
+
124
+
125
+ def get_plugin_details(plugin_name: str) -> Optional[Dict[str, Any]]:
126
+ """
127
+ Get detailed information about a specific plugin.
128
+
129
+ Args:
130
+ plugin_name: Name of the plugin to get details for
131
+
132
+ Returns:
133
+ Dictionary of detailed plugin information or None if not found
134
+ """
135
+ all_plugins = get_available_plugins()
136
+
137
+ # Find the plugin by name
138
+ for plugin in all_plugins:
139
+ if plugin.name.lower() == plugin_name.lower():
140
+ # Convert dataclass to dict and add additional details
141
+ plugin_dict = {
142
+ "name": plugin.name,
143
+ "description": plugin.description,
144
+ "github_url": plugin.github_url,
145
+ "version": plugin.version,
146
+ "author": plugin.author,
147
+ "tags": plugin.tags,
148
+ "created_at": plugin.created_at,
149
+ }
150
+ return plugin_dict
151
+
152
+ return None
153
+
154
+
155
+ def get_github_url_by_name(plugin_name: str) -> Optional[str]:
156
+ """
157
+ Get a plugin's GitHub URL by its name.
158
+
159
+ Args:
160
+ plugin_name: Name of the plugin
161
+
162
+ Returns:
163
+ GitHub URL of the plugin or None if not found
164
+ """
165
+ plugin_details = get_plugin_details(plugin_name)
166
+ if plugin_details:
167
+ return plugin_details.get("github_url")
168
+ return None
edsl/prompts/__init__.py CHANGED
@@ -1,2 +1,4 @@
1
1
  # from edsl.prompts.registry import get_classes
2
2
  from .prompt import Prompt
3
+
4
+ __all__ = ["Prompt"]
@@ -1,17 +1,119 @@
1
1
  from ..base import BaseException
2
2
 
3
3
  class PromptError(BaseException):
4
- pass
4
+ """
5
+ Base exception class for all prompt-related errors.
6
+
7
+ This is the parent class for all exceptions related to prompt processing,
8
+ rendering, and validation in the EDSL framework.
9
+ """
10
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/prompts.html"
5
11
 
6
12
 
7
13
  class TemplateRenderError(PromptError):
8
- "TODO: Move to exceptions file"
9
- pass
14
+ """
15
+ Exception raised when template rendering fails.
16
+
17
+ This exception is raised when:
18
+ - Template rendering exceeds the maximum nesting level (potential infinite loop)
19
+ - Template variables cannot be correctly substituted
20
+ - The template contains syntax errors
21
+
22
+ To fix this error:
23
+ 1. Check for circular references in your template variables
24
+ 2. Ensure all referenced variables are available in the template context
25
+ 3. Verify template syntax follows the correct format
26
+
27
+ Examples:
28
+ ```python
29
+ # Circular reference causing infinite loop
30
+ prompt.add_variable("var1", "{var2}")
31
+ prompt.add_variable("var2", "{var1}")
32
+ prompt.render() # Raises TemplateRenderError
33
+ ```
34
+ """
35
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/prompts.html#template-variables"
10
36
 
11
37
 
12
38
  class PromptBadQuestionTypeError(PromptError):
13
- pass
39
+ """
40
+ Exception raised when an invalid question type is used with a prompt.
41
+
42
+ This exception is for cases where a prompt is configured for a specific
43
+ question type but is used with an incompatible question.
44
+
45
+ To fix this error:
46
+ 1. Ensure you're using the correct question type with the prompt
47
+ 2. Check if the prompt needs to be updated to support the question type
48
+
49
+ Note: This exception is defined but not currently used in the codebase.
50
+ It raises Exception("not used") to indicate this state.
51
+ """
52
+ def __init__(self, message="Invalid question type for this prompt", **kwargs):
53
+ super().__init__(message, **kwargs)
14
54
 
15
55
 
16
56
  class PromptBadLanguageModelTypeError(PromptError):
17
- pass
57
+ """
58
+ Exception raised when an incompatible language model is used with a prompt.
59
+
60
+ This exception is for cases where a prompt requires specific language model
61
+ capabilities that the provided model doesn't support.
62
+
63
+ To fix this error:
64
+ 1. Use a different language model that supports the required capabilities
65
+ 2. Modify the prompt to work with the available language model
66
+
67
+ Note: This exception is defined but not currently used in the codebase.
68
+ It raises Exception("not used") to indicate this state.
69
+ """
70
+ def __init__(self, message="Incompatible language model for this prompt", **kwargs):
71
+ super().__init__(message, **kwargs)
72
+
73
+
74
+ class PromptValueError(PromptError):
75
+ """
76
+ Exception raised when there's an invalid value in prompt operations.
77
+
78
+ This exception occurs when:
79
+ - A path to a template folder is invalid
80
+ - Invalid parameters are provided for prompt configuration
81
+ - Other validation errors during prompt operations
82
+
83
+ To fix this error:
84
+ 1. Check that file paths are valid and accessible
85
+ 2. Verify that parameter values are within expected ranges or formats
86
+ 3. Ensure that all required prompt attributes are properly set
87
+
88
+ Examples:
89
+ ```python
90
+ # Invalid path for loading templates
91
+ prompt = Prompt.from_folder("/nonexistent/path") # Raises PromptValueError
92
+ ```
93
+ """
94
+ def __init__(self, message="Invalid value in prompt operation", **kwargs):
95
+ super().__init__(message, **kwargs)
96
+
97
+
98
+ class PromptImplementationError(PromptError):
99
+ """
100
+ Exception raised when a required method or feature is not implemented.
101
+
102
+ This exception occurs when:
103
+ - An abstract method is not implemented by a prompt subclass
104
+ - A feature is requested that is not implemented for a specific prompt type
105
+
106
+ To fix this error:
107
+ 1. Implement the required method in your prompt subclass
108
+ 2. Use a different prompt type that supports the requested feature
109
+ 3. Check for updates that might add the missing functionality
110
+
111
+ Examples:
112
+ ```python
113
+ # Calling an unimplemented method
114
+ custom_prompt = CustomPrompt()
115
+ custom_prompt.unimplemented_method() # Raises PromptImplementationError
116
+ ```
117
+ """
118
+ def __init__(self, message="Required method or feature is not implemented", **kwargs):
119
+ super().__init__(message, **kwargs)
edsl/prompts/prompt.py CHANGED
@@ -2,13 +2,16 @@ from __future__ import annotations
2
2
  from typing import Any, List, Union, Dict, Optional
3
3
  from pathlib import Path
4
4
  import time
5
+ import logging
5
6
  from functools import lru_cache
6
7
 
7
8
  from jinja2 import Environment, meta, Undefined
8
9
 
9
- from .exceptions import TemplateRenderError
10
+ from .exceptions import TemplateRenderError, PromptValueError, PromptImplementationError
10
11
  from ..base import PersistenceMixin, RepresentationMixin
11
12
 
13
+ logger = logging.getLogger(__name__)
14
+
12
15
  MAX_NESTING = 100
13
16
 
14
17
  class PreserveUndefined(Undefined):
@@ -55,6 +58,12 @@ def _make_hashable(value):
55
58
  return frozenset((k, _make_hashable(v)) for k, v in value.items())
56
59
  return value
57
60
 
61
+ @lru_cache(maxsize=1024)
62
+ def _compile_template(text: str):
63
+ """Compile a Jinja template with caching."""
64
+ env = make_env()
65
+ return env.from_string(text)
66
+
58
67
  @lru_cache(maxsize=1024)
59
68
  def _cached_render(text: str, frozen_replacements: frozenset) -> str:
60
69
  """Cached version of template rendering with frozen replacements."""
@@ -135,14 +144,13 @@ class Prompt(PersistenceMixin, RepresentationMixin):
135
144
  # Convert path_to_folder to a Path object if it's a string
136
145
  if path_to_folder is None:
137
146
  from importlib import resources
138
- import os
139
147
 
140
148
  path_to_folder = resources.path("edsl.questions", "prompt_templates")
141
149
 
142
150
  try:
143
151
  folder_path = Path(path_to_folder)
144
152
  except Exception as e:
145
- raise ValueError(f"Invalid path: {path_to_folder}. Error: {e}")
153
+ raise PromptValueError(f"Invalid path: {path_to_folder}. Error: {e}")
146
154
 
147
155
  with open(folder_path.joinpath(file_name), "r") as f:
148
156
  text = f.read()
@@ -306,7 +314,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
306
314
  # Provide access to the 'vars' object inside the template.
307
315
  env.globals['vars'] = template_vars
308
316
 
309
- previous_text = None
317
+ # Start with the original text
310
318
  current_text = text
311
319
 
312
320
  for _ in range(MAX_NESTING):
@@ -317,7 +325,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
317
325
  # No more changes, return final text with captured variables.
318
326
  return rendered_text, template_vars.get_all()
319
327
 
320
- previous_text = current_text
328
+ # Update current_text for next iteration
321
329
  current_text = rendered_text
322
330
 
323
331
  raise TemplateRenderError(
@@ -363,7 +371,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
363
371
  >>> p = Prompt("Hello, {{person}}")
364
372
  """
365
373
 
366
- raise NotImplementedError("This method should be implemented by the subclass.")
374
+ raise PromptImplementationError("This method should be implemented by the subclass.")
367
375
  start = time.time()
368
376
 
369
377
  # Build all the components
@@ -1,5 +1,4 @@
1
1
  from typing import Optional
2
- from ..prompts import Prompt
3
2
 
4
3
 
5
4
  class HTMLQuestion:
@@ -81,23 +80,18 @@ class HTMLQuestion:
81
80
  "include_question_name": include_question_name,
82
81
  }
83
82
  except Exception as e:
84
- raise ValueError(
83
+ from .exceptions import QuestionValueError
84
+ raise QuestionValueError(
85
85
  f"Error rendering question: params = {params}, error = {e}"
86
86
  )
87
87
  rendered_html = base_template.render(**params)
88
88
 
89
89
  if iframe:
90
- import html
91
- from IPython.display import display, HTML
92
-
90
+ from ..display import display_html
91
+
93
92
  height = height or 200
94
93
  width = width or 600
95
- escaped_output = html.escape(rendered_html)
96
- # escaped_output = rendered_html
97
- iframe = f""""
98
- <iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
99
- """
100
- display(HTML(iframe))
94
+ display_html(rendered_html, width=width, height=height, as_iframe=True)
101
95
  return None
102
96
 
103
97
  return rendered_html
edsl/questions/Quick.py CHANGED
@@ -3,7 +3,6 @@ from edsl import (
3
3
  QuestionMultipleChoice,
4
4
  Survey,
5
5
  QuestionList,
6
- Question,
7
6
  )
8
7
 
9
8
 
@@ -128,6 +128,8 @@ from .derived.question_top_k import QuestionTopK
128
128
  from .exceptions import QuestionScenarioRenderError
129
129
 
130
130
  __all__ = [
131
+ # Exceptions
132
+ "QuestionScenarioRenderError",
131
133
  # Schema and metadata
132
134
  "Settings",
133
135
  "RegisterQuestionsMeta",