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.
- 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 +129 -38
- 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.48.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.48.dist-info/RECORD +0 -347
- {edsl-0.1.48.dist-info → edsl-0.1.50.dist-info}/LICENSE +0 -0
- {edsl-0.1.48.dist-info → edsl-0.1.50.dist-info}/WHEEL +0 -0
@@ -1,7 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Dict, Any, Optional,
|
2
|
+
from typing import Dict, Any, Optional, TYPE_CHECKING
|
3
3
|
from functools import cached_property
|
4
|
-
import time
|
5
4
|
import logging
|
6
5
|
|
7
6
|
from ..prompts import Prompt
|
@@ -20,7 +19,6 @@ if TYPE_CHECKING:
|
|
20
19
|
from ..agents import Agent
|
21
20
|
from ..language_models import LanguageModel
|
22
21
|
from ..surveys.memory import MemoryPlan
|
23
|
-
from ..questions import QuestionBase
|
24
22
|
from ..scenarios import Scenario
|
25
23
|
|
26
24
|
logger = logging.getLogger(__name__)
|
@@ -438,8 +436,6 @@ class PromptConstructor:
|
|
438
436
|
|
439
437
|
def get_prompts(self) -> Dict[str, Any]:
|
440
438
|
"""Get the prompts for the question."""
|
441
|
-
start = time.time()
|
442
|
-
|
443
439
|
# Build all the components
|
444
440
|
agent_instructions = self.agent_instructions_prompt
|
445
441
|
agent_persona = self.agent_persona_prompt
|
@@ -76,19 +76,22 @@ class PromptPlan:
|
|
76
76
|
system_prompt_order = (system_prompt_order,)
|
77
77
|
|
78
78
|
if not isinstance(user_prompt_order, tuple):
|
79
|
-
|
79
|
+
from edsl.invigilators.exceptions import InvigilatorTypeError
|
80
|
+
raise InvigilatorTypeError(
|
80
81
|
f"Expected a tuple, but got {type(user_prompt_order).__name__}"
|
81
82
|
)
|
82
83
|
|
83
84
|
if not isinstance(system_prompt_order, tuple):
|
84
|
-
|
85
|
+
from edsl.invigilators.exceptions import InvigilatorTypeError
|
86
|
+
raise InvigilatorTypeError(
|
85
87
|
f"Expected a tuple, but got {type(system_prompt_order).__name__}"
|
86
88
|
)
|
87
89
|
|
88
90
|
self.user_prompt_order = self._convert_to_enum(user_prompt_order)
|
89
91
|
self.system_prompt_order = self._convert_to_enum(system_prompt_order)
|
90
92
|
if not self._is_valid_plan():
|
91
|
-
|
93
|
+
from edsl.invigilators.exceptions import InvigilatorValueError
|
94
|
+
raise InvigilatorValueError(
|
92
95
|
"Invalid plan: must contain each value of PromptComponent exactly once."
|
93
96
|
)
|
94
97
|
|
@@ -109,7 +112,8 @@ class PromptPlan:
|
|
109
112
|
# check is valid components passed
|
110
113
|
component_strings = set([pc.value for pc in PromptComponent])
|
111
114
|
if not set(kwargs.keys()) == component_strings:
|
112
|
-
|
115
|
+
from edsl.invigilators.exceptions import InvigilatorValueError
|
116
|
+
raise InvigilatorValueError(
|
113
117
|
f"Invalid components passed: {set(kwargs.keys())} but expected {PromptComponent}"
|
114
118
|
)
|
115
119
|
|
@@ -1,5 +1,7 @@
|
|
1
|
-
from jinja2 import Environment
|
2
|
-
from typing import List,
|
1
|
+
from jinja2 import Environment
|
2
|
+
from typing import List, Union
|
3
|
+
|
4
|
+
import edsl.scenarios.scenario # noqa: F401
|
3
5
|
|
4
6
|
|
5
7
|
def extract_template_variables(ast) -> List[Union[str, tuple]]:
|
@@ -61,7 +63,7 @@ class QuestionOptionProcessor:
|
|
61
63
|
|
62
64
|
return cls(scenario, prior_answers_dict)
|
63
65
|
|
64
|
-
def __init__(self, scenario: 'Scenario', prior_answers_dict: dict):
|
66
|
+
def __init__(self, scenario: 'edsl.scenarios.scenario.Scenario', prior_answers_dict: dict):
|
65
67
|
# This handles cases where the question has {{ scenario.key }} - eventually
|
66
68
|
# we might not allow 'naked' scenario keys w/o the scenario prefix
|
67
69
|
#new_scenario = scenario.copy()
|
@@ -106,9 +108,11 @@ class QuestionOptionProcessor:
|
|
106
108
|
undeclared_variables = extract_template_variables(parsed_content)
|
107
109
|
|
108
110
|
if not undeclared_variables:
|
109
|
-
|
111
|
+
from edsl.invigilators.exceptions import InvigilatorValueError
|
112
|
+
raise InvigilatorValueError("No variables found in template string")
|
110
113
|
if len(undeclared_variables) > 1:
|
111
|
-
|
114
|
+
from edsl.invigilators.exceptions import InvigilatorValueError
|
115
|
+
raise InvigilatorValueError("Multiple variables found in template string")
|
112
116
|
|
113
117
|
return undeclared_variables[0]
|
114
118
|
|
@@ -156,7 +156,8 @@ class QuestionTemplateReplacementsBuilder:
|
|
156
156
|
if var == "scenario":
|
157
157
|
# If we find a scenario variable, we need to check for nested references
|
158
158
|
# Create a modified template with just {{ scenario.* }} expressions to isolate them
|
159
|
-
|
159
|
+
# Using a template format for reference, not actually used
|
160
|
+
_ = "".join([
|
160
161
|
"{% for key, value in scenario.items() %}{{ key }}{% endfor %}"
|
161
162
|
])
|
162
163
|
try:
|
@@ -168,7 +169,7 @@ class QuestionTemplateReplacementsBuilder:
|
|
168
169
|
for key in scenario_refs:
|
169
170
|
if key in scenario_file_keys:
|
170
171
|
question_file_keys.append(key)
|
171
|
-
except:
|
172
|
+
except Exception:
|
172
173
|
# If there's any issue parsing, just continue with what we have
|
173
174
|
pass
|
174
175
|
|
edsl/jobs/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from .jobs import Jobs
|
2
|
-
from .jobs import RunConfig, RunParameters, RunEnvironment
|
3
|
-
from .remote_inference import JobsRemoteInferenceHandler
|
4
|
-
from .jobs_runner_status import JobsRunnerStatusBase
|
2
|
+
from .jobs import RunConfig, RunParameters, RunEnvironment # noqa: F401
|
3
|
+
from .remote_inference import JobsRemoteInferenceHandler # noqa: F401
|
4
|
+
from .jobs_runner_status import JobsRunnerStatusBase # noqa: F401
|
5
5
|
|
6
6
|
|
7
7
|
__all__ = ["Jobs"]
|
@@ -14,22 +14,23 @@ from edsl.data_transfer_models import EDSLResultObjectInput
|
|
14
14
|
from ..results import Result
|
15
15
|
from ..interviews import Interview
|
16
16
|
from ..config import Config
|
17
|
+
from .data_structures import RunConfig
|
18
|
+
|
17
19
|
config = Config()
|
18
20
|
|
19
21
|
if TYPE_CHECKING:
|
20
22
|
from ..jobs import Jobs
|
21
23
|
|
22
|
-
from .data_structures import RunConfig
|
23
|
-
|
24
24
|
@dataclass
|
25
25
|
class InterviewResult:
|
26
26
|
"""Container for the result of an interview along with metadata.
|
27
|
-
|
27
|
+
|
28
28
|
Attributes:
|
29
29
|
result: The Result object containing the interview answers
|
30
30
|
interview: The Interview object used to conduct the interview
|
31
31
|
order: The original position of this interview in the processing queue
|
32
32
|
"""
|
33
|
+
|
33
34
|
result: Result
|
34
35
|
interview: Interview
|
35
36
|
order: int
|
@@ -38,10 +39,10 @@ class InterviewResult:
|
|
38
39
|
class AsyncInterviewRunner:
|
39
40
|
"""
|
40
41
|
Runs interviews asynchronously with controlled concurrency.
|
41
|
-
|
42
|
+
|
42
43
|
This class manages the parallel execution of multiple interviews while
|
43
44
|
respecting concurrency limits and handling errors appropriately.
|
44
|
-
|
45
|
+
|
45
46
|
Examples:
|
46
47
|
>>> from unittest.mock import MagicMock, AsyncMock
|
47
48
|
>>> mock_jobs = MagicMock()
|
@@ -52,13 +53,13 @@ class AsyncInterviewRunner:
|
|
52
53
|
>>> isinstance(runner._initialized, asyncio.Event)
|
53
54
|
True
|
54
55
|
"""
|
55
|
-
|
56
|
+
|
56
57
|
MAX_CONCURRENT = int(config.EDSL_MAX_CONCURRENT_TASKS)
|
57
58
|
|
58
59
|
def __init__(self, jobs: "Jobs", run_config: RunConfig):
|
59
60
|
"""
|
60
61
|
Initialize the AsyncInterviewRunner.
|
61
|
-
|
62
|
+
|
62
63
|
Args:
|
63
64
|
jobs: The Jobs object that generates interviews
|
64
65
|
run_config: Configuration for running the interviews
|
@@ -70,13 +71,13 @@ class AsyncInterviewRunner:
|
|
70
71
|
def _expand_interviews(self) -> Generator["Interview", None, None]:
|
71
72
|
"""
|
72
73
|
Create multiple copies of each interview based on the run configuration.
|
73
|
-
|
74
|
+
|
74
75
|
This method expands interviews for repeated runs and ensures each has
|
75
76
|
the proper cache configuration.
|
76
|
-
|
77
|
+
|
77
78
|
Yields:
|
78
79
|
Interview objects ready to be conducted
|
79
|
-
|
80
|
+
|
80
81
|
Examples:
|
81
82
|
>>> from unittest.mock import MagicMock
|
82
83
|
>>> mock_jobs = MagicMock()
|
@@ -105,16 +106,16 @@ class AsyncInterviewRunner:
|
|
105
106
|
) -> Tuple["Result", "Interview"]:
|
106
107
|
"""
|
107
108
|
Asynchronously conduct a single interview.
|
108
|
-
|
109
|
+
|
109
110
|
This method performs the interview and creates a Result object with
|
110
111
|
the extracted answers and model responses.
|
111
|
-
|
112
|
+
|
112
113
|
Args:
|
113
114
|
interview: The interview to conduct
|
114
|
-
|
115
|
+
|
115
116
|
Returns:
|
116
117
|
Tuple containing the Result object and the Interview object
|
117
|
-
|
118
|
+
|
118
119
|
Notes:
|
119
120
|
'extracted_answers' contains the processed and validated answers
|
120
121
|
from the interview, which may differ from the raw model output.
|
@@ -137,14 +138,14 @@ class AsyncInterviewRunner:
|
|
137
138
|
) -> AsyncGenerator[tuple[Result, Interview], None]:
|
138
139
|
"""
|
139
140
|
Run all interviews asynchronously and yield results as they complete.
|
140
|
-
|
141
|
+
|
141
142
|
This method processes interviews in chunks based on MAX_CONCURRENT,
|
142
143
|
maintaining controlled concurrency while yielding results as soon as
|
143
144
|
they become available.
|
144
|
-
|
145
|
+
|
145
146
|
Yields:
|
146
147
|
Tuples of (Result, Interview) as interviews complete
|
147
|
-
|
148
|
+
|
148
149
|
Notes:
|
149
150
|
- Uses structured concurrency patterns for proper resource management
|
150
151
|
- Handles exceptions according to the run configuration
|
@@ -159,11 +160,11 @@ class AsyncInterviewRunner:
|
|
159
160
|
try:
|
160
161
|
result, interview = await self._conduct_interview(interview)
|
161
162
|
self.run_config.environment.jobs_runner_status.add_completed_interview(
|
162
|
-
|
163
|
+
interview
|
163
164
|
)
|
164
165
|
result.order = idx
|
165
166
|
return InterviewResult(result, interview, idx)
|
166
|
-
except Exception
|
167
|
+
except Exception:
|
167
168
|
if self.run_config.parameters.stop_on_exception:
|
168
169
|
raise
|
169
170
|
return None
|
@@ -187,7 +188,7 @@ class AsyncInterviewRunner:
|
|
187
188
|
for result in (r for r in results if r is not None):
|
188
189
|
yield result.result, result.interview
|
189
190
|
|
190
|
-
except Exception
|
191
|
+
except Exception:
|
191
192
|
if self.run_config.parameters.stop_on_exception:
|
192
193
|
raise
|
193
194
|
continue
|
@@ -200,5 +201,6 @@ class AsyncInterviewRunner:
|
|
200
201
|
|
201
202
|
|
202
203
|
if __name__ == "__main__":
|
203
|
-
import doctest
|
204
|
-
|
204
|
+
import doctest
|
205
|
+
|
206
|
+
doctest.testmod()
|
@@ -2,6 +2,7 @@ import warnings
|
|
2
2
|
from typing import TYPE_CHECKING
|
3
3
|
from edsl.scenarios import ScenarioList
|
4
4
|
from edsl.surveys import Survey
|
5
|
+
from edsl.jobs.exceptions import JobsCompatibilityError
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
7
8
|
from edsl.surveys.Survey import Survey
|
@@ -34,19 +35,19 @@ class CheckSurveyScenarioCompatibility:
|
|
34
35
|
>>> s = Scenario({'plop': "A", 'poo': "B"})
|
35
36
|
>>> j = Jobs(survey = Survey(questions=[q])).by(s)
|
36
37
|
>>> cs = CheckSurveyScenarioCompatibility(j.survey, j.scenarios)
|
37
|
-
>>> cs.check(strict = True)
|
38
|
+
>>> cs.check(strict = True) # doctest: +ELLIPSIS
|
38
39
|
Traceback (most recent call last):
|
39
40
|
...
|
40
|
-
|
41
|
+
edsl.jobs.exceptions.JobsCompatibilityError: The following parameters are in the scenarios but not in the survey: {'plop'}...
|
41
42
|
|
42
43
|
>>> q = QuestionFreeText(question_text = "Hello", question_name = "ugly_question")
|
43
44
|
>>> s = Scenario({'ugly_question': "B"})
|
44
45
|
>>> from edsl.scenarios import ScenarioList
|
45
46
|
>>> cs = CheckSurveyScenarioCompatibility(Survey(questions=[q]), ScenarioList([s]))
|
46
|
-
>>> cs.check()
|
47
|
+
>>> cs.check() # doctest: +ELLIPSIS
|
47
48
|
Traceback (most recent call last):
|
48
49
|
...
|
49
|
-
|
50
|
+
edsl.jobs.exceptions.JobsCompatibilityError: The following names are in both the survey question_names and the scenario keys: {'ugly_question'}. This will create issues...
|
50
51
|
"""
|
51
52
|
survey_parameters: set = self.survey.parameters
|
52
53
|
scenario_parameters: set = self.scenarios.parameters
|
@@ -59,7 +60,7 @@ class CheckSurveyScenarioCompatibility:
|
|
59
60
|
):
|
60
61
|
msg0 = f"The following names are in both the survey question_names and the scenario keys: {intersection}. This will create issues."
|
61
62
|
|
62
|
-
raise
|
63
|
+
raise JobsCompatibilityError(msg0)
|
63
64
|
|
64
65
|
if in_survey_but_not_in_scenarios := survey_parameters - scenario_parameters:
|
65
66
|
msg1 = f"The following parameters are in the survey but not in the scenarios: {in_survey_but_not_in_scenarios}"
|
@@ -69,7 +70,7 @@ class CheckSurveyScenarioCompatibility:
|
|
69
70
|
if msg1 or msg2:
|
70
71
|
message = "\n".join(filter(None, [msg1, msg2]))
|
71
72
|
if strict:
|
72
|
-
raise
|
73
|
+
raise JobsCompatibilityError(message)
|
73
74
|
else:
|
74
75
|
if warn:
|
75
76
|
warnings.warn(message)
|
edsl/jobs/data_structures.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
from typing import Optional, Literal
|
1
|
+
from typing import Optional, Literal, TYPE_CHECKING
|
2
2
|
from dataclasses import dataclass, asdict
|
3
|
+
from collections import UserDict
|
4
|
+
from edsl.data_transfer_models import EDSLResultObjectInput
|
3
5
|
|
4
6
|
# from edsl.data_transfer_models import VisibilityType
|
5
7
|
from ..caching import Cache
|
@@ -9,6 +11,10 @@ from ..base import Base
|
|
9
11
|
|
10
12
|
from .jobs_runner_status import JobsRunnerStatus
|
11
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from ..questions.question_base import QuestionBase
|
16
|
+
from ..surveys import Survey
|
17
|
+
|
12
18
|
VisibilityType = Literal["private", "public", "unlisted"]
|
13
19
|
|
14
20
|
@dataclass
|
@@ -157,9 +163,6 @@ class RunConfig:
|
|
157
163
|
Additional data structures for working with job results and answers.
|
158
164
|
"""
|
159
165
|
|
160
|
-
from collections import UserDict
|
161
|
-
from edsl.data_transfer_models import EDSLResultObjectInput
|
162
|
-
|
163
166
|
|
164
167
|
class Answers(UserDict):
|
165
168
|
"""
|
edsl/jobs/exceptions.py
CHANGED
@@ -1,26 +1,195 @@
|
|
1
|
-
from textwrap import dedent
|
2
1
|
|
3
2
|
from ..base import BaseException
|
4
3
|
|
5
4
|
class JobsErrors(BaseException):
|
6
|
-
|
5
|
+
"""
|
6
|
+
Base exception class for all job-related errors.
|
7
|
+
|
8
|
+
This is the parent class for all exceptions related to job execution
|
9
|
+
in the EDSL framework. It provides a common type for catching any
|
10
|
+
job-specific error.
|
11
|
+
"""
|
12
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/jobs.html"
|
7
13
|
|
8
14
|
|
9
15
|
class JobsRunError(JobsErrors):
|
10
|
-
|
16
|
+
"""
|
17
|
+
Exception raised when a job fails to run correctly.
|
18
|
+
|
19
|
+
This exception indicates issues with job execution such as:
|
20
|
+
- Configuration problems before job execution
|
21
|
+
- Resource allocation failures
|
22
|
+
- Job termination due to internal errors
|
23
|
+
|
24
|
+
To fix this error:
|
25
|
+
1. Check the job configuration for any invalid parameters
|
26
|
+
2. Ensure all required resources (models, agents, etc.) are available
|
27
|
+
3. Verify that dependent services are accessible
|
28
|
+
|
29
|
+
Note: This exception is currently not used actively in the codebase,
|
30
|
+
but is kept for potential future use and test cases.
|
31
|
+
"""
|
32
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/jobs.html"
|
11
33
|
|
12
34
|
|
13
35
|
class MissingRemoteInferenceError(JobsErrors):
|
14
|
-
|
36
|
+
"""
|
37
|
+
Exception raised when remote inference is required but not configured.
|
38
|
+
|
39
|
+
This exception occurs when:
|
40
|
+
- A job requires remote inference capabilities but they're not available
|
41
|
+
- Credentials for remote inference are missing or invalid
|
42
|
+
|
43
|
+
To fix this error:
|
44
|
+
1. Set up remote inference configuration in your environment
|
45
|
+
2. Provide valid API keys for the required inference service
|
46
|
+
|
47
|
+
Note: This exception is defined but not currently used in the codebase.
|
48
|
+
It raises Exception("not used") to indicate this state.
|
49
|
+
"""
|
50
|
+
def __init__(self, message="Remote inference configuration is missing", **kwargs):
|
51
|
+
super().__init__(message, **kwargs)
|
15
52
|
|
16
53
|
|
17
|
-
class InterviewError(
|
18
|
-
|
54
|
+
class InterviewError(JobsErrors):
|
55
|
+
"""
|
56
|
+
Base exception class for all interview-related errors.
|
57
|
+
|
58
|
+
This exception serves as the parent class for specific interview errors
|
59
|
+
and handles cases where an interview process fails for reasons not covered
|
60
|
+
by more specific exceptions.
|
61
|
+
"""
|
62
|
+
|
63
|
+
def __init__(self, message="An error occurred during the interview process", **kwargs):
|
64
|
+
super().__init__(message, **kwargs)
|
65
|
+
self.message = message
|
19
66
|
|
20
67
|
|
21
68
|
class InterviewErrorPriorTaskCanceled(InterviewError):
|
22
|
-
|
69
|
+
"""
|
70
|
+
Exception raised when a task cannot run because a dependent task failed.
|
71
|
+
|
72
|
+
This exception is raised in task pipelines when a prerequisite task
|
73
|
+
fails or is canceled, preventing dependent tasks from executing.
|
74
|
+
|
75
|
+
When you encounter this error:
|
76
|
+
1. Check the error logs for information about the failed dependent task
|
77
|
+
2. Fix any issues with the prerequisite task first before retrying
|
78
|
+
3. If using custom task dependencies, verify that the dependency chain is correct
|
79
|
+
"""
|
80
|
+
|
81
|
+
def __init__(self, message="Cannot run this task because a required prior task was canceled or failed"):
|
82
|
+
super().__init__(message)
|
23
83
|
|
24
84
|
|
25
85
|
class InterviewTimeoutError(InterviewError):
|
26
|
-
|
86
|
+
"""
|
87
|
+
Exception raised when an interview operation times out.
|
88
|
+
|
89
|
+
This exception indicates that a model call or other operation
|
90
|
+
during an interview process took too long to complete.
|
91
|
+
|
92
|
+
To fix this error:
|
93
|
+
1. Check your network connection if using remote models
|
94
|
+
2. Consider increasing timeout settings if dealing with complex prompts
|
95
|
+
3. Try a different model provider if consistently experiencing timeouts
|
96
|
+
|
97
|
+
Note: While defined here, the codebase currently uses LanguageModelNoResponseError
|
98
|
+
to handle timeouts in actual operation.
|
99
|
+
"""
|
100
|
+
|
101
|
+
def __init__(self, message="The interview operation timed out", **kwargs):
|
102
|
+
super().__init__(message, **kwargs)
|
103
|
+
|
104
|
+
|
105
|
+
class JobsValueError(JobsErrors):
|
106
|
+
"""
|
107
|
+
Exception raised when there's an invalid value in job-related operations.
|
108
|
+
|
109
|
+
This exception indicates that a parameter or value used in job configuration
|
110
|
+
or execution is invalid, out of range, or otherwise inappropriate.
|
111
|
+
|
112
|
+
Common causes include:
|
113
|
+
- Invalid question names in job configuration
|
114
|
+
- Incompatible survey and scenario combinations
|
115
|
+
- Invalid parameter values for job construction
|
116
|
+
|
117
|
+
To fix this error:
|
118
|
+
1. Check the parameter values in your job configuration
|
119
|
+
2. Ensure that all question names exist in the survey
|
120
|
+
3. Verify that survey and scenario combinations are compatible
|
121
|
+
"""
|
122
|
+
|
123
|
+
def __init__(self, message="Invalid value in jobs module", **kwargs):
|
124
|
+
super().__init__(message, **kwargs)
|
125
|
+
|
126
|
+
|
127
|
+
class JobsCompatibilityError(JobsErrors):
|
128
|
+
"""
|
129
|
+
Exception raised when there are compatibility issues between components.
|
130
|
+
|
131
|
+
This exception indicates that the components being used together (like
|
132
|
+
surveys and scenarios) are not compatible with each other for the requested
|
133
|
+
operation.
|
134
|
+
|
135
|
+
To fix this error:
|
136
|
+
1. Check that your survey and scenario are compatible
|
137
|
+
2. Ensure all referenced questions exist in the survey
|
138
|
+
3. Verify that scenario fields match expected inputs for questions
|
139
|
+
"""
|
140
|
+
|
141
|
+
def __init__(self, message="Compatibility issue between job components", **kwargs):
|
142
|
+
super().__init__(message, **kwargs)
|
143
|
+
|
144
|
+
|
145
|
+
class JobsImplementationError(JobsErrors):
|
146
|
+
"""
|
147
|
+
Exception raised when a required method or feature is not implemented.
|
148
|
+
|
149
|
+
This exception indicates that a method or functionality expected to be
|
150
|
+
available is not implemented, typically in abstract classes or
|
151
|
+
interfaces that require concrete implementation.
|
152
|
+
|
153
|
+
To fix this error:
|
154
|
+
1. Implement the required method in your subclass
|
155
|
+
2. Use a different implementation that provides the required functionality
|
156
|
+
3. Check for updates to the library that might implement this feature
|
157
|
+
"""
|
158
|
+
|
159
|
+
def __init__(self, message="Required method or feature is not implemented", **kwargs):
|
160
|
+
super().__init__(message, **kwargs)
|
161
|
+
|
162
|
+
|
163
|
+
class RemoteInferenceError(JobsErrors):
|
164
|
+
"""
|
165
|
+
Exception raised when there are issues with remote inference.
|
166
|
+
|
167
|
+
This exception indicates problems with remote inference configuration,
|
168
|
+
connection, or execution. It can occur when a remote job fails to create
|
169
|
+
or execute properly.
|
170
|
+
|
171
|
+
To fix this error:
|
172
|
+
1. Check your remote inference configuration
|
173
|
+
2. Verify API keys and authentication are correct
|
174
|
+
3. Ensure the remote service is available and responsive
|
175
|
+
"""
|
176
|
+
|
177
|
+
def __init__(self, message="Remote inference operation failed", **kwargs):
|
178
|
+
super().__init__(message, **kwargs)
|
179
|
+
|
180
|
+
|
181
|
+
class JobsTypeError(JobsErrors):
|
182
|
+
"""
|
183
|
+
Exception raised when there's a type mismatch in job-related operations.
|
184
|
+
|
185
|
+
This exception indicates that a parameter or value is of the wrong type
|
186
|
+
for the operation being performed.
|
187
|
+
|
188
|
+
To fix this error:
|
189
|
+
1. Check the types of parameters you're passing to job functions
|
190
|
+
2. Ensure that you're using the correct types as defined in the API
|
191
|
+
3. Convert parameters to the expected types if necessary
|
192
|
+
"""
|
193
|
+
|
194
|
+
def __init__(self, message="Type mismatch in jobs module", **kwargs):
|
195
|
+
super().__init__(message, **kwargs)
|
edsl/jobs/fetch_invigilator.py
CHANGED