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,4 +1,3 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
2
1
|
from dataclasses import dataclass
|
3
2
|
|
4
3
|
|
@@ -46,12 +45,6 @@ class InstructionHandler:
|
|
46
45
|
['intro']
|
47
46
|
>>> [i.name for i in s._relevant_instructions(q3)]
|
48
47
|
[]
|
49
|
-
|
50
|
-
>>> i_change = ChangeInstruction(keep = ["poop"], drop = [])
|
51
|
-
>>> s = Survey([q1, i, q2, i_change])
|
52
|
-
Traceback (most recent call last):
|
53
|
-
...
|
54
|
-
ValueError: ChangeInstruction change_instruction_0 references instruction poop which does not exist.
|
55
48
|
"""
|
56
49
|
from .instruction import Instruction
|
57
50
|
from .change_instruction import ChangeInstruction
|
@@ -70,7 +63,8 @@ class InstructionHandler:
|
|
70
63
|
num_change_instructions += 1
|
71
64
|
for prior_instruction in entry.keep + entry.drop:
|
72
65
|
if prior_instruction not in instruction_names_to_instructions:
|
73
|
-
|
66
|
+
from edsl.instructions.exceptions import InstructionValueError
|
67
|
+
raise InstructionValueError(
|
74
68
|
f"ChangeInstruction {entry.name} references instruction {prior_instruction} which does not exist."
|
75
69
|
)
|
76
70
|
instructions_run_length += 1
|
@@ -83,7 +77,8 @@ class InstructionHandler:
|
|
83
77
|
instructions_run_length = 0
|
84
78
|
true_questions.append(entry)
|
85
79
|
else:
|
86
|
-
|
80
|
+
from edsl.instructions.exceptions import InstructionValueError
|
81
|
+
raise InstructionValueError(
|
87
82
|
f"Entry {repr(entry)} is not a QuestionBase or an Instruction."
|
88
83
|
)
|
89
84
|
|
edsl/interviews/ReportErrors.py
CHANGED
edsl/interviews/__init__.py
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
from .
|
1
|
+
from .interview import Interview
|
2
2
|
from .interview_status_dictionary import InterviewStatusDictionary
|
3
3
|
from .interview_status_log import InterviewStatusLog
|
4
|
-
from .
|
4
|
+
from .interview_task_manager import InterviewTaskManager
|
5
|
+
|
6
|
+
__all__ = [
|
7
|
+
"InterviewTaskManager",
|
8
|
+
"InterviewStatusDictionary",
|
9
|
+
"InterviewStatusLog",
|
10
|
+
"Interview"
|
11
|
+
]
|
@@ -1,21 +1,19 @@
|
|
1
|
-
import copy
|
2
1
|
import asyncio
|
3
|
-
|
4
|
-
from typing import
|
2
|
+
import copy
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Union
|
5
4
|
|
6
5
|
if TYPE_CHECKING:
|
6
|
+
from ..invigilators.invigilator_base import InvigilatorBase
|
7
|
+
from ..key_management import KeyLookup
|
7
8
|
from ..questions import QuestionBase
|
8
9
|
from .interview import Interview
|
9
|
-
from ..key_management import KeyLookup
|
10
|
-
|
11
|
-
from ..surveys.base import EndOfSurvey
|
12
|
-
from ..tasks import TaskStatus
|
13
10
|
|
11
|
+
from ..data_transfer_models import EDSLResultObjectInput
|
14
12
|
from ..jobs.fetch_invigilator import FetchInvigilator
|
15
13
|
from ..language_models.exceptions import LanguageModelNoResponseError
|
16
14
|
from ..questions.exceptions import QuestionAnswerValidationError
|
17
|
-
from ..
|
18
|
-
|
15
|
+
from ..surveys.base import EndOfSurvey
|
16
|
+
from ..tasks import TaskStatus
|
19
17
|
from .exception_tracking import InterviewExceptionEntry
|
20
18
|
|
21
19
|
|
@@ -46,7 +44,7 @@ class SkipHandler:
|
|
46
44
|
| self.interview.agent["traits"]
|
47
45
|
)
|
48
46
|
return self.skip_function(current_question_index, combined_answers)
|
49
|
-
|
47
|
+
|
50
48
|
def _current_info_env(self) -> dict[str, Any]:
|
51
49
|
"""
|
52
50
|
- The current answers are "generated_tokens" and "comment"
|
@@ -112,7 +110,7 @@ class SkipHandler:
|
|
112
110
|
if next_question_index > (current_question_index + 1):
|
113
111
|
cancel_between(current_question_index + 1, next_question_index)
|
114
112
|
|
115
|
-
|
113
|
+
|
116
114
|
|
117
115
|
|
118
116
|
class AnswerQuestionFunctionConstructor:
|
@@ -175,11 +173,11 @@ class AnswerQuestionFunctionConstructor:
|
|
175
173
|
) -> "EDSLResultObjectInput":
|
176
174
|
|
177
175
|
from tenacity import (
|
176
|
+
RetryError,
|
178
177
|
retry,
|
178
|
+
retry_if_exception_type,
|
179
179
|
stop_after_attempt,
|
180
180
|
wait_exponential,
|
181
|
-
retry_if_exception_type,
|
182
|
-
RetryError,
|
183
181
|
)
|
184
182
|
|
185
183
|
@retry(
|
@@ -1,10 +1,11 @@
|
|
1
|
-
from collections import UserDict
|
2
|
-
import traceback
|
3
1
|
import datetime
|
4
2
|
import json
|
3
|
+
import traceback
|
4
|
+
from collections import UserDict
|
5
5
|
|
6
6
|
from ..invigilators import InvigilatorBase
|
7
7
|
|
8
|
+
|
8
9
|
class InterviewExceptionEntry:
|
9
10
|
"""Class to record an exception that occurred during the interview."""
|
10
11
|
|
@@ -69,8 +70,8 @@ class InterviewExceptionEntry:
|
|
69
70
|
|
70
71
|
>>> entry = InterviewExceptionEntry.example()
|
71
72
|
"""
|
72
|
-
from ..questions import QuestionFreeText
|
73
73
|
from ..language_models import LanguageModel
|
74
|
+
from ..questions import QuestionFreeText
|
74
75
|
|
75
76
|
m = LanguageModel.example(test_model=True)
|
76
77
|
q = QuestionFreeText.example(exception_to_throw=ValueError)
|
@@ -128,12 +129,11 @@ class InterviewExceptionEntry:
|
|
128
129
|
|
129
130
|
@property
|
130
131
|
def html_traceback(self) -> str:
|
132
|
+
from io import StringIO
|
133
|
+
|
131
134
|
from rich.console import Console
|
132
|
-
from rich.table import Table
|
133
135
|
from rich.traceback import Traceback
|
134
136
|
|
135
|
-
from io import StringIO
|
136
|
-
|
137
137
|
html_output = StringIO()
|
138
138
|
|
139
139
|
console = Console(file=html_output, record=True)
|
@@ -218,7 +218,6 @@ class InterviewExceptionEntry:
|
|
218
218
|
return cls(exception=exception, invigilator=invigilator)
|
219
219
|
|
220
220
|
|
221
|
-
|
222
221
|
class InterviewExceptionCollection(UserDict):
|
223
222
|
"""A collection of exceptions that occurred during the interview."""
|
224
223
|
|
@@ -231,6 +230,14 @@ class InterviewExceptionCollection(UserDict):
|
|
231
230
|
"""Return a list of unfixed exceptions."""
|
232
231
|
return {k: v for k, v in self.data.items() if k not in self.fixed}
|
233
232
|
|
233
|
+
def num_exceptions(self) -> int:
|
234
|
+
"""Return the total number of exceptions."""
|
235
|
+
return sum(len(v) for v in self.data.values())
|
236
|
+
|
237
|
+
def num_unfixed_exceptions(self) -> int:
|
238
|
+
"""Return the number of unfixed exceptions."""
|
239
|
+
return sum(len(v) for v in self.unfixed_exceptions().values())
|
240
|
+
|
234
241
|
def num_unfixed(self) -> int:
|
235
242
|
"""Return a list of unfixed questions."""
|
236
243
|
return len([k for k in self.data.keys() if k not in self.fixed])
|
@@ -0,0 +1,79 @@
|
|
1
|
+
"""
|
2
|
+
Exceptions specific to the interviews module.
|
3
|
+
|
4
|
+
This module defines custom exception classes for all interview-related errors
|
5
|
+
in the EDSL framework, ensuring consistent error handling and user feedback.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from ..base import BaseException
|
9
|
+
|
10
|
+
|
11
|
+
class InterviewError(BaseException):
|
12
|
+
"""
|
13
|
+
Base exception class for all interview-related errors.
|
14
|
+
|
15
|
+
This is the parent class for all exceptions related to interview creation,
|
16
|
+
execution, and management in the EDSL framework.
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
```python
|
20
|
+
# Usually not raised directly, but through subclasses
|
21
|
+
# For example, when accessing incomplete tasks
|
22
|
+
interview.get_completed_task("incomplete_task") # Would raise InterviewTaskError
|
23
|
+
```
|
24
|
+
"""
|
25
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
|
26
|
+
|
27
|
+
|
28
|
+
class InterviewTaskError(InterviewError):
|
29
|
+
"""
|
30
|
+
Exception raised when there's an issue with interview tasks.
|
31
|
+
|
32
|
+
This exception occurs when:
|
33
|
+
- Attempting to access tasks that are not complete
|
34
|
+
- Task execution fails due to errors in the model response
|
35
|
+
- Task dependencies are not satisfied
|
36
|
+
|
37
|
+
Examples:
|
38
|
+
```python
|
39
|
+
# Attempting to access an incomplete task
|
40
|
+
interview.get_completed_task("incomplete_task") # Raises InterviewTaskError
|
41
|
+
```
|
42
|
+
"""
|
43
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
|
44
|
+
|
45
|
+
|
46
|
+
class InterviewStatusError(InterviewError):
|
47
|
+
"""
|
48
|
+
Exception raised when there's an issue with interview status management.
|
49
|
+
|
50
|
+
This exception occurs when:
|
51
|
+
- Invalid operations are performed on interview status objects
|
52
|
+
- Incompatible status objects are combined
|
53
|
+
- Status tracking encounters inconsistent states
|
54
|
+
|
55
|
+
Examples:
|
56
|
+
```python
|
57
|
+
# Attempting to add incompatible status objects
|
58
|
+
status_dict + incompatible_object # Raises InterviewStatusError
|
59
|
+
```
|
60
|
+
"""
|
61
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/interviews.html"
|
62
|
+
|
63
|
+
|
64
|
+
class InterviewTokenError(InterviewError):
|
65
|
+
"""
|
66
|
+
Exception raised when there's an issue with token estimation or usage.
|
67
|
+
|
68
|
+
This exception occurs when:
|
69
|
+
- Invalid inputs are provided to token estimators
|
70
|
+
- Token calculation fails due to unsupported prompt types
|
71
|
+
- Token limits are exceeded during an interview
|
72
|
+
|
73
|
+
Examples:
|
74
|
+
```python
|
75
|
+
# Providing an invalid prompt type to token estimator
|
76
|
+
estimator.estimate_tokens(invalid_prompt) # Raises InterviewTokenError
|
77
|
+
```
|
78
|
+
"""
|
79
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/token_usage.html"
|
edsl/interviews/interview.py
CHANGED
@@ -14,36 +14,38 @@ the individual API calls to language models, with support for caching and distri
|
|
14
14
|
"""
|
15
15
|
|
16
16
|
from __future__ import annotations
|
17
|
+
|
17
18
|
import asyncio
|
18
19
|
import copy
|
19
20
|
from dataclasses import dataclass
|
21
|
+
from typing import TYPE_CHECKING, Any, Generator, List, Optional, Type
|
20
22
|
|
21
|
-
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from ..jobs.data_structures import RunConfig
|
25
|
+
from .interview_status_log import InterviewStatusLog
|
22
26
|
|
23
|
-
# from jobs module
|
24
|
-
from ..jobs.data_structures import Answers
|
27
|
+
# from jobs module
|
25
28
|
from ..buckets import ModelBuckets
|
29
|
+
from ..jobs.data_structures import Answers
|
26
30
|
from ..jobs.fetch_invigilator import FetchInvigilator
|
27
|
-
from ..utilities.utilities import dict_hash
|
28
31
|
from ..surveys import Survey
|
32
|
+
from ..utilities.utilities import dict_hash
|
29
33
|
|
30
|
-
# from interviews module
|
34
|
+
# from interviews module
|
31
35
|
from .answering_function import AnswerQuestionFunctionConstructor
|
36
|
+
from .exception_tracking import InterviewExceptionCollection, InterviewExceptionEntry
|
37
|
+
from .interview_status_dictionary import InterviewStatusDictionary
|
32
38
|
from .interview_task_manager import InterviewTaskManager
|
33
39
|
from .request_token_estimator import RequestTokenEstimator
|
34
|
-
from .interview_status_dictionary import InterviewStatusDictionary
|
35
|
-
from .exception_tracking import InterviewExceptionCollection, InterviewExceptionEntry
|
36
|
-
|
37
40
|
|
38
41
|
if TYPE_CHECKING:
|
39
42
|
from ..agents import Agent
|
40
|
-
from ..surveys import Survey
|
41
|
-
from ..scenarios import Scenario
|
42
43
|
from ..caching import Cache
|
44
|
+
from ..invigilators import InvigilatorBase
|
43
45
|
from ..language_models import LanguageModel
|
46
|
+
from ..scenarios import Scenario
|
47
|
+
from ..surveys import Survey
|
44
48
|
from ..tokens import InterviewTokenUsage
|
45
|
-
from ..invigilators import InvigilatorBase
|
46
|
-
from ..key_management import KeyLookup
|
47
49
|
|
48
50
|
|
49
51
|
@dataclass
|
@@ -277,15 +279,15 @@ class Interview:
|
|
277
279
|
"iteration": self.iteration,
|
278
280
|
"exceptions": {},
|
279
281
|
}
|
280
|
-
|
282
|
+
|
281
283
|
# Optionally include exceptions
|
282
284
|
if include_exceptions:
|
283
285
|
d["exceptions"] = self.exceptions.to_dict()
|
284
|
-
|
286
|
+
|
285
287
|
# Include custom indices if present
|
286
288
|
if hasattr(self, "indices"):
|
287
289
|
d["indices"] = self.indices
|
288
|
-
|
290
|
+
|
289
291
|
return d
|
290
292
|
|
291
293
|
@classmethod
|
@@ -304,9 +306,9 @@ class Interview:
|
|
304
306
|
"""
|
305
307
|
# Import necessary classes
|
306
308
|
from ..agents import Agent
|
307
|
-
from ..surveys import Survey
|
308
|
-
from ..scenarios import Scenario
|
309
309
|
from ..language_models import LanguageModel
|
310
|
+
from ..scenarios import Scenario
|
311
|
+
from ..surveys import Survey
|
310
312
|
|
311
313
|
# Deserialize each component
|
312
314
|
agent = Agent.from_dict(d["agent"])
|
@@ -314,7 +316,7 @@ class Interview:
|
|
314
316
|
scenario = Scenario.from_dict(d["scenario"])
|
315
317
|
model = LanguageModel.from_dict(d["model"])
|
316
318
|
iteration = d["iteration"]
|
317
|
-
|
319
|
+
|
318
320
|
# Prepare constructor parameters
|
319
321
|
params = {
|
320
322
|
"agent": agent,
|
@@ -323,19 +325,19 @@ class Interview:
|
|
323
325
|
"model": model,
|
324
326
|
"iteration": iteration,
|
325
327
|
}
|
326
|
-
|
328
|
+
|
327
329
|
# Add optional indices if present
|
328
330
|
if "indices" in d:
|
329
331
|
params["indices"] = d["indices"]
|
330
|
-
|
332
|
+
|
331
333
|
# Create the interview instance
|
332
334
|
interview = cls(**params)
|
333
|
-
|
335
|
+
|
334
336
|
# Restore exceptions if present
|
335
337
|
if "exceptions" in d:
|
336
338
|
exceptions = InterviewExceptionCollection.from_dict(d["exceptions"])
|
337
339
|
interview.exceptions = exceptions
|
338
|
-
|
340
|
+
|
339
341
|
return interview
|
340
342
|
|
341
343
|
def __hash__(self) -> int:
|
@@ -416,7 +418,7 @@ class Interview:
|
|
416
418
|
>>> run_config.parameters.stop_on_exception = True
|
417
419
|
>>> result, _ = asyncio.run(i.async_conduct_interview(run_config))
|
418
420
|
"""
|
419
|
-
from ..jobs import RunConfig,
|
421
|
+
from ..jobs import RunConfig, RunEnvironment, RunParameters
|
420
422
|
|
421
423
|
if run_config is None:
|
422
424
|
run_config = RunConfig(
|
@@ -503,7 +505,7 @@ class Interview:
|
|
503
505
|
result = invigilator.get_failed_task_result(
|
504
506
|
failure_reason="Task was skipped."
|
505
507
|
)
|
506
|
-
except asyncio.CancelledError
|
508
|
+
except asyncio.CancelledError: # task was cancelled
|
507
509
|
result = invigilator.get_failed_task_result(
|
508
510
|
failure_reason="Task was cancelled."
|
509
511
|
)
|
@@ -520,7 +522,8 @@ class Interview:
|
|
520
522
|
|
521
523
|
for task, invigilator in zip(tasks, invigilators):
|
522
524
|
if not task.done():
|
523
|
-
|
525
|
+
from edsl.interviews.exceptions import InterviewTaskError
|
526
|
+
raise InterviewTaskError(f"Task {task.get_name()} is not done.")
|
524
527
|
|
525
528
|
yield handle_task(task, invigilator)
|
526
529
|
|
@@ -608,9 +611,9 @@ class Interview:
|
|
608
611
|
True
|
609
612
|
"""
|
610
613
|
from ..agents import Agent
|
611
|
-
from ..surveys import Survey
|
612
|
-
from ..scenarios import Scenario
|
613
614
|
from ..language_models import LanguageModel
|
615
|
+
from ..scenarios import Scenario
|
616
|
+
from ..surveys import Survey
|
614
617
|
|
615
618
|
# Define a simple direct answering method that always returns "yes"
|
616
619
|
def f(self, question, scenario):
|
@@ -622,12 +625,12 @@ class Interview:
|
|
622
625
|
survey = Survey.example()
|
623
626
|
scenario = Scenario.example()
|
624
627
|
model = LanguageModel.example()
|
625
|
-
|
628
|
+
|
626
629
|
# If we want an interview that throws exceptions, configure it accordingly
|
627
630
|
if throw_exception:
|
628
631
|
model = LanguageModel.example(test_model=True, throw_exception=True)
|
629
632
|
agent = Agent.example() # Without direct answering method
|
630
|
-
|
633
|
+
|
631
634
|
# Create and return the interview
|
632
635
|
return Interview(agent=agent, survey=survey, scenario=scenario, model=model)
|
633
636
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
|
2
3
|
from collections import UserDict
|
3
|
-
from typing import
|
4
|
+
from typing import Dict, Union
|
4
5
|
|
5
6
|
from ..tasks.task_status_enum import TaskStatus, get_enum_from_string
|
6
7
|
|
@@ -26,7 +27,8 @@ class InterviewStatusDictionary(UserDict):
|
|
26
27
|
) -> "InterviewStatusDictionary":
|
27
28
|
"""Adds two InterviewStatusDictionaries together."""
|
28
29
|
if not isinstance(other, InterviewStatusDictionary):
|
29
|
-
|
30
|
+
from edsl.interviews.exceptions import InterviewStatusError
|
31
|
+
raise InterviewStatusError(f"Can't add {type(other)} to InterviewStatusDictionary")
|
30
32
|
new_dict = {}
|
31
33
|
for key in self.keys():
|
32
34
|
new_dict[key] = self[key] + other[key]
|
@@ -2,6 +2,7 @@ from collections import UserDict
|
|
2
2
|
|
3
3
|
from ..tasks.task_status_enum import TaskStatus, status_colors
|
4
4
|
|
5
|
+
|
5
6
|
class InterviewStatusLog(UserDict):
|
6
7
|
"""A dictionary of TaskStatusLog objects.
|
7
8
|
|
@@ -50,8 +51,8 @@ class InterviewStatusLog(UserDict):
|
|
50
51
|
def visualize(self, num_periods: int = 10) -> None:
|
51
52
|
"""Visualize the status matrix with outlined squares."""
|
52
53
|
import matplotlib.pyplot as plt
|
53
|
-
from matplotlib.colors import ListedColormap
|
54
54
|
import numpy as np
|
55
|
+
from matplotlib.colors import ListedColormap
|
55
56
|
from matplotlib.patches import Rectangle
|
56
57
|
|
57
58
|
# Define your custom colormap
|
@@ -1,12 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
|
2
3
|
import asyncio
|
3
|
-
from typing import
|
4
|
+
from typing import TYPE_CHECKING
|
4
5
|
|
5
6
|
if TYPE_CHECKING:
|
6
7
|
from ..questions import QuestionBase
|
7
8
|
from ..tokens import InterviewTokenUsage
|
8
|
-
from . import InterviewStatusDictionary
|
9
|
-
from . import InterviewStatusLog
|
9
|
+
from . import InterviewStatusDictionary, InterviewStatusLog
|
10
10
|
|
11
11
|
|
12
12
|
class InterviewTaskManager:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from ..jobs.fetch_invigilator import FetchInvigilator
|
2
2
|
from ..scenarios import FileStore
|
3
3
|
|
4
|
+
|
4
5
|
class RequestTokenEstimator:
|
5
6
|
"""Estimate the number of tokens that will be required to run the focal task."""
|
6
7
|
|
@@ -25,7 +26,8 @@ class RequestTokenEstimator:
|
|
25
26
|
if isinstance(file, FileStore):
|
26
27
|
file_tokens += file.size * 0.25
|
27
28
|
else:
|
28
|
-
|
29
|
+
from edsl.interviews.exceptions import InterviewTokenError
|
30
|
+
raise InterviewTokenError(f"Prompt is of type {type(prompt)}")
|
29
31
|
return len(combined_text) / 4.0 + file_tokens
|
30
32
|
|
31
33
|
|
edsl/interviews/statistics.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from collections import UserDict
|
2
|
-
from typing import
|
3
|
-
from collections import UserDict
|
2
|
+
from typing import DefaultDict
|
4
3
|
|
5
4
|
from ..tokens import InterviewTokenUsage
|
6
5
|
|
@@ -22,7 +21,7 @@ class InterviewStatistic(UserDict):
|
|
22
21
|
>>> InterviewStatistic._format_number(1000, 1, "sec.")
|
23
22
|
'1,000.0 sec.'
|
24
23
|
"""
|
25
|
-
if
|
24
|
+
if isinstance(number, str):
|
26
25
|
return number
|
27
26
|
else:
|
28
27
|
return f"{number:,.{digits}f}" + " " + units
|
edsl/invigilators/__init__.py
CHANGED
@@ -35,4 +35,10 @@ from .invigilator_base import InvigilatorBase
|
|
35
35
|
from .invigilators import InvigilatorFunctional
|
36
36
|
from .prompt_constructor import PromptConstructor
|
37
37
|
|
38
|
-
|
38
|
+
__all__ = [
|
39
|
+
'InvigilatorAI',
|
40
|
+
'InvigilatorHuman',
|
41
|
+
'InvigilatorBase',
|
42
|
+
'InvigilatorFunctional',
|
43
|
+
'PromptConstructor'
|
44
|
+
]
|
@@ -0,0 +1,79 @@
|
|
1
|
+
"""
|
2
|
+
Exceptions specific to the invigilators module.
|
3
|
+
|
4
|
+
This module defines custom exception classes for all invigilator-related errors
|
5
|
+
in the EDSL framework, ensuring consistent error handling and user feedback.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from ..base import BaseException
|
9
|
+
|
10
|
+
|
11
|
+
class InvigilatorError(BaseException):
|
12
|
+
"""
|
13
|
+
Base exception class for all invigilator-related errors.
|
14
|
+
|
15
|
+
This is the parent class for all exceptions related to invigilator creation,
|
16
|
+
execution, and management in the EDSL framework.
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
```python
|
20
|
+
# Usually not raised directly, but through subclasses
|
21
|
+
# For example, when attempting to use an unimplemented feature
|
22
|
+
invigilator.encode_image(image_path) # Would raise InvigilatorNotImplementedError
|
23
|
+
```
|
24
|
+
"""
|
25
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
|
26
|
+
|
27
|
+
|
28
|
+
class InvigilatorTypeError(InvigilatorError):
|
29
|
+
"""
|
30
|
+
Exception raised when there's a type mismatch in invigilator operations.
|
31
|
+
|
32
|
+
This exception occurs when:
|
33
|
+
- Invalid input types are provided to invigilator methods
|
34
|
+
- Template variables have incompatible types
|
35
|
+
- Return value types don't match expected types
|
36
|
+
|
37
|
+
Examples:
|
38
|
+
```python
|
39
|
+
# Providing an invalid type to an invigilator method
|
40
|
+
invigilator.process_options(123) # Raises InvigilatorTypeError if expected a dict
|
41
|
+
```
|
42
|
+
"""
|
43
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
|
44
|
+
|
45
|
+
|
46
|
+
class InvigilatorValueError(InvigilatorError):
|
47
|
+
"""
|
48
|
+
Exception raised when invalid values are provided to invigilator operations.
|
49
|
+
|
50
|
+
This exception occurs when:
|
51
|
+
- Template variables are missing or invalid
|
52
|
+
- Option values don't match expected patterns
|
53
|
+
- Configuration values are outside allowed ranges
|
54
|
+
|
55
|
+
Examples:
|
56
|
+
```python
|
57
|
+
# Template with invalid variables
|
58
|
+
invigilator.process_template("Hello {{ missing }}") # Raises InvigilatorValueError
|
59
|
+
```
|
60
|
+
"""
|
61
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
|
62
|
+
|
63
|
+
|
64
|
+
class InvigilatorNotImplementedError(InvigilatorError):
|
65
|
+
"""
|
66
|
+
Exception raised when attempting to use an unimplemented feature.
|
67
|
+
|
68
|
+
This exception occurs when:
|
69
|
+
- Calling methods that are not implemented in a specific invigilator
|
70
|
+
- Using features only available in certain invigilator types
|
71
|
+
- Using planned functionality that is not yet available
|
72
|
+
|
73
|
+
Examples:
|
74
|
+
```python
|
75
|
+
# Attempting to use image encoding when not implemented
|
76
|
+
invigilator.encode_image(image_path) # Raises InvigilatorNotImplementedError
|
77
|
+
```
|
78
|
+
"""
|
79
|
+
relevant_doc = "https://docs.expectedparrot.com/en/latest/agents.html#invigilators"
|
@@ -2,14 +2,12 @@
|
|
2
2
|
from abc import ABC, abstractmethod
|
3
3
|
import asyncio
|
4
4
|
from typing import Coroutine, Dict, Any, Optional, TYPE_CHECKING
|
5
|
-
from typing import
|
5
|
+
from typing import Literal
|
6
6
|
|
7
7
|
from ..utilities.decorators import sync_wrapper
|
8
8
|
from ..questions.exceptions import QuestionAnswerValidationError
|
9
9
|
from ..data_transfer_models import AgentResponseDict, EDSLResultObjectInput
|
10
10
|
from ..utilities.decorators import jupyter_nb_handler
|
11
|
-
from ..data_transfer_models import AgentResponseDict
|
12
|
-
from ..data_transfer_models import EDSLResultObjectInput
|
13
11
|
|
14
12
|
from .prompt_constructor import PromptConstructor
|
15
13
|
from .prompt_helpers import PromptPlan
|
@@ -18,13 +16,10 @@ if TYPE_CHECKING:
|
|
18
16
|
from ..prompts import Prompt
|
19
17
|
from ..scenarios import Scenario
|
20
18
|
from ..surveys import Survey
|
21
|
-
from ..prompts import Prompt
|
22
19
|
from ..caching import Cache
|
23
20
|
from ..questions import QuestionBase
|
24
|
-
from ..scenarios import Scenario
|
25
21
|
from ..surveys.memory import MemoryPlan
|
26
22
|
from ..language_models import LanguageModel
|
27
|
-
from ..surveys import Survey
|
28
23
|
from ..agents import Agent
|
29
24
|
from ..key_management import KeyLookup
|
30
25
|
|
@@ -171,14 +166,14 @@ class InvigilatorBase(ABC):
|
|
171
166
|
"""
|
172
167
|
data = {
|
173
168
|
"answer": None,
|
174
|
-
"generated_tokens": None,
|
169
|
+
"generated_tokens": getattr(self, "generated_tokens", None),
|
175
170
|
"comment": failure_reason,
|
176
171
|
"question_name": self.question.question_name,
|
177
172
|
"prompts": self.get_prompts(),
|
178
|
-
"cached_response": None,
|
179
|
-
"raw_model_response": None,
|
180
|
-
"cache_used": None,
|
181
|
-
"cache_key": None,
|
173
|
+
"cached_response": getattr(self, "cached_response", None),
|
174
|
+
"raw_model_response": getattr(self, "raw_model_response", None),
|
175
|
+
"cache_used": getattr(self, "cache_used", None),
|
176
|
+
"cache_key": getattr(self, "cache_key", None),
|
182
177
|
}
|
183
178
|
return EDSLResultObjectInput(**data)
|
184
179
|
|
@@ -285,7 +280,8 @@ class InvigilatorAI(InvigilatorBase):
|
|
285
280
|
}
|
286
281
|
if "encoded_image" in prompts:
|
287
282
|
params["encoded_image"] = prompts["encoded_image"]
|
288
|
-
|
283
|
+
from edsl.invigilators.exceptions import InvigilatorNotImplementedError
|
284
|
+
raise InvigilatorNotImplementedError("encoded_image not implemented")
|
289
285
|
|
290
286
|
if "files_list" in prompts:
|
291
287
|
params["files_list"] = prompts["files_list"]
|