edsl 0.1.49__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.
- edsl/__init__.py +124 -53
- edsl/__version__.py +1 -1
- edsl/agents/agent.py +21 -21
- edsl/agents/agent_list.py +2 -5
- edsl/agents/exceptions.py +119 -5
- edsl/base/__init__.py +10 -35
- edsl/base/base_class.py +71 -36
- edsl/base/base_exception.py +204 -0
- edsl/base/data_transfer_models.py +1 -1
- edsl/base/exceptions.py +94 -0
- edsl/buckets/__init__.py +15 -1
- edsl/buckets/bucket_collection.py +3 -4
- edsl/buckets/exceptions.py +75 -0
- edsl/buckets/model_buckets.py +1 -2
- edsl/buckets/token_bucket.py +11 -6
- edsl/buckets/token_bucket_api.py +1 -2
- edsl/buckets/token_bucket_client.py +9 -7
- edsl/caching/cache.py +7 -2
- edsl/caching/cache_entry.py +10 -9
- edsl/caching/exceptions.py +113 -7
- edsl/caching/remote_cache_sync.py +1 -2
- edsl/caching/sql_dict.py +17 -12
- edsl/cli.py +43 -0
- edsl/config/config_class.py +30 -6
- edsl/conversation/Conversation.py +3 -2
- edsl/conversation/exceptions.py +58 -0
- edsl/conversation/mug_negotiation.py +0 -2
- edsl/coop/__init__.py +20 -1
- edsl/coop/coop.py +120 -29
- edsl/coop/exceptions.py +188 -9
- edsl/coop/price_fetcher.py +3 -6
- edsl/coop/utils.py +4 -6
- edsl/dataset/__init__.py +5 -4
- edsl/dataset/dataset.py +53 -43
- edsl/dataset/dataset_operations_mixin.py +86 -72
- edsl/dataset/dataset_tree.py +9 -5
- edsl/dataset/display/table_display.py +0 -2
- edsl/dataset/display/table_renderers.py +0 -1
- edsl/dataset/exceptions.py +125 -0
- edsl/dataset/file_exports.py +18 -11
- edsl/dataset/r/ggplot.py +13 -6
- edsl/display/__init__.py +27 -0
- edsl/display/core.py +147 -0
- edsl/display/plugin.py +189 -0
- edsl/display/utils.py +52 -0
- edsl/inference_services/__init__.py +9 -1
- edsl/inference_services/available_model_cache_handler.py +1 -1
- edsl/inference_services/available_model_fetcher.py +4 -5
- edsl/inference_services/data_structures.py +9 -6
- edsl/inference_services/exceptions.py +132 -1
- edsl/inference_services/inference_service_abc.py +2 -2
- edsl/inference_services/inference_services_collection.py +2 -6
- edsl/inference_services/registry.py +4 -3
- edsl/inference_services/service_availability.py +2 -1
- edsl/inference_services/services/anthropic_service.py +4 -1
- edsl/inference_services/services/aws_bedrock.py +13 -12
- edsl/inference_services/services/azure_ai.py +12 -10
- edsl/inference_services/services/deep_infra_service.py +1 -4
- edsl/inference_services/services/deep_seek_service.py +1 -5
- edsl/inference_services/services/google_service.py +6 -2
- edsl/inference_services/services/groq_service.py +1 -1
- edsl/inference_services/services/mistral_ai_service.py +4 -2
- edsl/inference_services/services/ollama_service.py +1 -1
- edsl/inference_services/services/open_ai_service.py +7 -5
- edsl/inference_services/services/perplexity_service.py +6 -2
- edsl/inference_services/services/test_service.py +8 -7
- edsl/inference_services/services/together_ai_service.py +2 -3
- edsl/inference_services/services/xai_service.py +1 -1
- edsl/instructions/__init__.py +1 -1
- edsl/instructions/change_instruction.py +3 -2
- edsl/instructions/exceptions.py +61 -0
- edsl/instructions/instruction.py +5 -2
- edsl/instructions/instruction_collection.py +2 -1
- edsl/instructions/instruction_handler.py +4 -9
- edsl/interviews/ReportErrors.py +0 -3
- edsl/interviews/__init__.py +9 -2
- edsl/interviews/answering_function.py +11 -13
- edsl/interviews/exception_tracking.py +14 -7
- edsl/interviews/exceptions.py +79 -0
- edsl/interviews/interview.py +32 -29
- edsl/interviews/interview_status_dictionary.py +4 -2
- edsl/interviews/interview_status_log.py +2 -1
- edsl/interviews/interview_task_manager.py +3 -3
- edsl/interviews/request_token_estimator.py +3 -1
- edsl/interviews/statistics.py +2 -3
- edsl/invigilators/__init__.py +7 -1
- edsl/invigilators/exceptions.py +79 -0
- edsl/invigilators/invigilator_base.py +0 -1
- edsl/invigilators/invigilators.py +8 -12
- edsl/invigilators/prompt_constructor.py +1 -5
- edsl/invigilators/prompt_helpers.py +8 -4
- edsl/invigilators/question_instructions_prompt_builder.py +1 -1
- edsl/invigilators/question_option_processor.py +9 -5
- edsl/invigilators/question_template_replacements_builder.py +3 -2
- edsl/jobs/__init__.py +3 -3
- edsl/jobs/async_interview_runner.py +24 -22
- edsl/jobs/check_survey_scenario_compatibility.py +7 -6
- edsl/jobs/data_structures.py +7 -4
- edsl/jobs/exceptions.py +177 -8
- edsl/jobs/fetch_invigilator.py +1 -1
- edsl/jobs/jobs.py +72 -67
- edsl/jobs/jobs_checks.py +2 -3
- edsl/jobs/jobs_component_constructor.py +2 -2
- edsl/jobs/jobs_pricing_estimation.py +3 -2
- edsl/jobs/jobs_remote_inference_logger.py +5 -4
- edsl/jobs/jobs_runner_asyncio.py +1 -2
- edsl/jobs/jobs_runner_status.py +8 -9
- edsl/jobs/remote_inference.py +26 -23
- edsl/jobs/results_exceptions_handler.py +8 -5
- edsl/key_management/__init__.py +3 -1
- edsl/key_management/exceptions.py +62 -0
- edsl/key_management/key_lookup.py +1 -1
- edsl/key_management/key_lookup_builder.py +37 -14
- edsl/key_management/key_lookup_collection.py +2 -0
- edsl/language_models/__init__.py +1 -1
- edsl/language_models/exceptions.py +302 -14
- edsl/language_models/language_model.py +4 -7
- edsl/language_models/model.py +4 -4
- edsl/language_models/model_list.py +1 -1
- edsl/language_models/price_manager.py +1 -1
- edsl/language_models/raw_response_handler.py +14 -9
- edsl/language_models/registry.py +17 -21
- edsl/language_models/repair.py +0 -6
- edsl/language_models/unused/fake_openai_service.py +0 -1
- edsl/load_plugins.py +69 -0
- edsl/logger.py +146 -0
- edsl/notebooks/notebook.py +1 -1
- edsl/notebooks/notebook_to_latex.py +0 -1
- edsl/plugins/__init__.py +63 -0
- edsl/plugins/built_in/export_example.py +50 -0
- edsl/plugins/built_in/pig_latin.py +67 -0
- edsl/plugins/cli.py +372 -0
- edsl/plugins/cli_typer.py +283 -0
- edsl/plugins/exceptions.py +31 -0
- edsl/plugins/hookspec.py +51 -0
- edsl/plugins/plugin_host.py +128 -0
- edsl/plugins/plugin_manager.py +633 -0
- edsl/plugins/plugins_registry.py +168 -0
- edsl/prompts/__init__.py +2 -0
- edsl/prompts/exceptions.py +107 -5
- edsl/prompts/prompt.py +14 -6
- edsl/questions/HTMLQuestion.py +5 -11
- edsl/questions/Quick.py +0 -1
- edsl/questions/__init__.py +2 -0
- edsl/questions/answer_validator_mixin.py +318 -318
- edsl/questions/compose_questions.py +2 -2
- edsl/questions/descriptors.py +10 -49
- edsl/questions/exceptions.py +278 -22
- edsl/questions/loop_processor.py +7 -5
- edsl/questions/prompt_templates/question_list.jinja +3 -0
- edsl/questions/question_base.py +14 -16
- edsl/questions/question_base_gen_mixin.py +2 -2
- edsl/questions/question_base_prompts_mixin.py +9 -3
- edsl/questions/question_budget.py +9 -5
- edsl/questions/question_check_box.py +3 -5
- edsl/questions/question_dict.py +171 -194
- edsl/questions/question_extract.py +1 -1
- edsl/questions/question_free_text.py +4 -6
- edsl/questions/question_functional.py +4 -3
- edsl/questions/question_list.py +36 -9
- edsl/questions/question_matrix.py +95 -61
- edsl/questions/question_multiple_choice.py +6 -4
- edsl/questions/question_numerical.py +2 -4
- edsl/questions/question_registry.py +4 -2
- edsl/questions/register_questions_meta.py +0 -1
- edsl/questions/response_validator_abc.py +7 -13
- edsl/questions/templates/dict/answering_instructions.jinja +1 -0
- edsl/questions/templates/rank/question_presentation.jinja +1 -1
- edsl/results/__init__.py +1 -1
- edsl/results/exceptions.py +141 -7
- edsl/results/report.py +0 -1
- edsl/results/result.py +4 -5
- edsl/results/results.py +10 -51
- edsl/results/results_selector.py +8 -4
- edsl/scenarios/PdfExtractor.py +2 -2
- edsl/scenarios/construct_download_link.py +69 -35
- edsl/scenarios/directory_scanner.py +33 -14
- edsl/scenarios/document_chunker.py +1 -1
- edsl/scenarios/exceptions.py +238 -14
- edsl/scenarios/file_methods.py +1 -1
- edsl/scenarios/file_store.py +7 -3
- edsl/scenarios/handlers/__init__.py +17 -0
- edsl/scenarios/handlers/docx_file_store.py +0 -5
- edsl/scenarios/handlers/pdf_file_store.py +0 -1
- edsl/scenarios/handlers/pptx_file_store.py +0 -5
- edsl/scenarios/handlers/py_file_store.py +0 -1
- edsl/scenarios/handlers/sql_file_store.py +1 -4
- edsl/scenarios/handlers/sqlite_file_store.py +0 -1
- edsl/scenarios/handlers/txt_file_store.py +1 -1
- edsl/scenarios/scenario.py +0 -1
- edsl/scenarios/scenario_list.py +152 -18
- edsl/scenarios/scenario_list_pdf_tools.py +1 -0
- edsl/scenarios/scenario_selector.py +0 -1
- edsl/surveys/__init__.py +3 -4
- edsl/surveys/dag/__init__.py +4 -2
- edsl/surveys/descriptors.py +1 -1
- edsl/surveys/edit_survey.py +1 -0
- edsl/surveys/exceptions.py +165 -9
- edsl/surveys/memory/__init__.py +5 -3
- edsl/surveys/memory/memory_management.py +1 -0
- edsl/surveys/memory/memory_plan.py +6 -15
- edsl/surveys/rules/__init__.py +5 -3
- edsl/surveys/rules/rule.py +1 -2
- edsl/surveys/rules/rule_collection.py +1 -1
- edsl/surveys/survey.py +12 -24
- edsl/surveys/survey_export.py +6 -3
- edsl/surveys/survey_flow_visualization.py +10 -1
- edsl/tasks/__init__.py +2 -0
- edsl/tasks/question_task_creator.py +3 -3
- edsl/tasks/task_creators.py +1 -3
- edsl/tasks/task_history.py +5 -7
- edsl/tasks/task_status_log.py +1 -2
- edsl/tokens/__init__.py +3 -1
- edsl/tokens/token_usage.py +1 -1
- edsl/utilities/__init__.py +21 -1
- edsl/utilities/decorators.py +1 -2
- edsl/utilities/markdown_to_docx.py +2 -2
- edsl/utilities/markdown_to_pdf.py +1 -1
- edsl/utilities/repair_functions.py +0 -1
- edsl/utilities/restricted_python.py +0 -1
- edsl/utilities/template_loader.py +2 -3
- edsl/utilities/utilities.py +8 -29
- {edsl-0.1.49.dist-info → edsl-0.1.50.dist-info}/METADATA +32 -2
- edsl-0.1.50.dist-info/RECORD +363 -0
- edsl-0.1.50.dist-info/entry_points.txt +3 -0
- edsl/dataset/smart_objects.py +0 -96
- edsl/exceptions/BaseException.py +0 -21
- edsl/exceptions/__init__.py +0 -54
- edsl/exceptions/configuration.py +0 -16
- edsl/exceptions/general.py +0 -34
- edsl/study/ObjectEntry.py +0 -173
- edsl/study/ProofOfWork.py +0 -113
- edsl/study/SnapShot.py +0 -80
- edsl/study/Study.py +0 -520
- edsl/study/__init__.py +0 -6
- edsl/utilities/interface.py +0 -135
- edsl-0.1.49.dist-info/RECORD +0 -347
- {edsl-0.1.49.dist-info → edsl-0.1.50.dist-info}/LICENSE +0 -0
- {edsl-0.1.49.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
edsl/prompts/exceptions.py
CHANGED
@@ -1,17 +1,119 @@
|
|
1
1
|
from ..base import BaseException
|
2
2
|
|
3
3
|
class PromptError(BaseException):
|
4
|
-
|
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
|
-
"
|
9
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
374
|
+
raise PromptImplementationError("This method should be implemented by the subclass.")
|
367
375
|
start = time.time()
|
368
376
|
|
369
377
|
# Build all the components
|
edsl/questions/HTMLQuestion.py
CHANGED
@@ -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
|
-
|
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
|
91
|
-
|
92
|
-
|
90
|
+
from ..display import display_html
|
91
|
+
|
93
92
|
height = height or 200
|
94
93
|
width = width or 600
|
95
|
-
|
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
edsl/questions/__init__.py
CHANGED