edsl 0.1.49__py3-none-any.whl → 0.1.51__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 +107 -0
- edsl/buckets/model_buckets.py +1 -2
- edsl/buckets/token_bucket.py +11 -6
- edsl/buckets/token_bucket_api.py +27 -12
- edsl/buckets/token_bucket_client.py +9 -7
- edsl/caching/cache.py +12 -4
- edsl/caching/cache_entry.py +10 -9
- edsl/caching/exceptions.py +113 -7
- edsl/caching/remote_cache_sync.py +6 -7
- edsl/caching/sql_dict.py +20 -14
- edsl/cli.py +43 -0
- edsl/config/__init__.py +1 -1
- edsl/config/config_class.py +32 -6
- edsl/conversation/Conversation.py +8 -4
- edsl/conversation/car_buying.py +1 -3
- edsl/conversation/exceptions.py +58 -0
- edsl/conversation/mug_negotiation.py +2 -8
- edsl/coop/__init__.py +28 -6
- edsl/coop/coop.py +120 -29
- edsl/coop/coop_functions.py +1 -1
- edsl/coop/ep_key_handling.py +1 -1
- edsl/coop/exceptions.py +188 -9
- edsl/coop/price_fetcher.py +5 -8
- edsl/coop/utils.py +4 -6
- edsl/dataset/__init__.py +5 -4
- edsl/dataset/dataset.py +177 -86
- edsl/dataset/dataset_operations_mixin.py +98 -76
- edsl/dataset/dataset_tree.py +11 -7
- edsl/dataset/display/table_display.py +0 -2
- edsl/dataset/display/table_renderers.py +6 -4
- 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 +5 -6
- edsl/inference_services/data_structures.py +10 -7
- 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 +4 -3
- 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 +7 -3
- 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 +7 -5
- edsl/instructions/exceptions.py +61 -0
- edsl/instructions/instruction.py +6 -2
- edsl/instructions/instruction_collection.py +6 -4
- edsl/instructions/instruction_handler.py +12 -15
- edsl/interviews/ReportErrors.py +0 -3
- edsl/interviews/__init__.py +9 -2
- edsl/interviews/answering_function.py +11 -13
- edsl/interviews/exception_tracking.py +15 -8
- edsl/interviews/exceptions.py +79 -0
- edsl/interviews/interview.py +33 -30
- edsl/interviews/interview_status_dictionary.py +4 -2
- edsl/interviews/interview_status_log.py +2 -1
- edsl/interviews/interview_task_manager.py +5 -5
- edsl/interviews/request_token_estimator.py +5 -2
- edsl/interviews/statistics.py +3 -4
- edsl/invigilators/__init__.py +7 -1
- edsl/invigilators/exceptions.py +79 -0
- edsl/invigilators/invigilator_base.py +0 -1
- edsl/invigilators/invigilators.py +9 -13
- 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 +42 -5
- edsl/jobs/async_interview_runner.py +25 -23
- edsl/jobs/check_survey_scenario_compatibility.py +11 -10
- edsl/jobs/data_structures.py +8 -5
- edsl/jobs/exceptions.py +177 -8
- edsl/jobs/fetch_invigilator.py +1 -1
- edsl/jobs/jobs.py +74 -69
- edsl/jobs/jobs_checks.py +6 -7
- edsl/jobs/jobs_component_constructor.py +4 -4
- edsl/jobs/jobs_pricing_estimation.py +4 -3
- edsl/jobs/jobs_remote_inference_logger.py +5 -4
- edsl/jobs/jobs_runner_asyncio.py +3 -4
- edsl/jobs/jobs_runner_status.py +8 -9
- edsl/jobs/remote_inference.py +27 -24
- edsl/jobs/results_exceptions_handler.py +10 -7
- 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 +9 -8
- 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/__init__.py +24 -1
- edsl/notebooks/exceptions.py +82 -0
- edsl/notebooks/notebook.py +7 -3
- edsl/notebooks/notebook_to_latex.py +1 -2
- 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 +24 -1
- edsl/prompts/exceptions.py +107 -5
- edsl/prompts/prompt.py +15 -7
- edsl/questions/HTMLQuestion.py +5 -11
- edsl/questions/Quick.py +0 -1
- edsl/questions/__init__.py +6 -4
- edsl/questions/answer_validator_mixin.py +318 -323
- edsl/questions/compose_questions.py +3 -3
- edsl/questions/descriptors.py +11 -50
- 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 +46 -19
- edsl/questions/question_base_gen_mixin.py +2 -2
- edsl/questions/question_base_prompts_mixin.py +13 -7
- edsl/questions/question_budget.py +503 -98
- edsl/questions/question_check_box.py +660 -160
- edsl/questions/question_dict.py +345 -194
- edsl/questions/question_extract.py +401 -61
- edsl/questions/question_free_text.py +80 -14
- edsl/questions/question_functional.py +119 -9
- edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
- edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
- edsl/questions/question_list.py +275 -28
- edsl/questions/question_matrix.py +643 -96
- edsl/questions/question_multiple_choice.py +219 -51
- edsl/questions/question_numerical.py +361 -32
- edsl/questions/question_rank.py +401 -124
- edsl/questions/question_registry.py +7 -5
- edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
- edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
- edsl/questions/register_questions_meta.py +2 -2
- edsl/questions/response_validator_abc.py +13 -15
- edsl/questions/response_validator_factory.py +10 -12
- 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 +1 -2
- edsl/results/result.py +11 -9
- edsl/results/results.py +480 -321
- 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 +1 -3
- edsl/scenarios/scenario_list.py +179 -27
- 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_css.py +3 -3
- edsl/surveys/survey_export.py +6 -3
- edsl/surveys/survey_flow_visualization.py +10 -1
- edsl/surveys/survey_simulator.py +2 -1
- edsl/tasks/__init__.py +23 -1
- edsl/tasks/exceptions.py +72 -0
- edsl/tasks/question_task_creator.py +3 -3
- edsl/tasks/task_creators.py +1 -3
- edsl/tasks/task_history.py +8 -10
- edsl/tasks/task_status_log.py +1 -2
- edsl/tokens/__init__.py +29 -1
- edsl/tokens/exceptions.py +37 -0
- edsl/tokens/interview_token_usage.py +3 -2
- edsl/tokens/token_usage.py +4 -3
- 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.51.dist-info}/METADATA +32 -2
- edsl-0.1.51.dist-info/RECORD +365 -0
- edsl-0.1.51.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/questions/derived/__init__.py +0 -0
- 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.51.dist-info}/LICENSE +0 -0
- {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/WHEEL +0 -0
edsl/instructions/instruction.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
2
|
from ..utilities.remove_edsl_version import remove_edsl_version
|
3
3
|
from ..base import RepresentationMixin
|
4
|
-
|
4
|
+
from typing import TYPE_CHECKING
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from ..surveys import Survey
|
5
8
|
|
6
9
|
|
7
10
|
class Instruction(RepresentationMixin):
|
@@ -11,6 +14,7 @@ class Instruction(RepresentationMixin):
|
|
11
14
|
self.name = name
|
12
15
|
self.text = text
|
13
16
|
self.preamble = preamble
|
17
|
+
self.pseudo_index = 0.0
|
14
18
|
|
15
19
|
def __str__(self):
|
16
20
|
return self.text
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING, Dict, List, Generator, Union
|
1
|
+
from typing import TYPE_CHECKING, Dict, List, Generator, Union, Tuple
|
2
2
|
from collections import UserDict
|
3
3
|
|
4
4
|
from .instruction import Instruction
|
@@ -40,12 +40,14 @@ class InstructionCollection(UserDict):
|
|
40
40
|
|
41
41
|
def _entries_before(
|
42
42
|
self, question_name
|
43
|
-
) ->
|
43
|
+
) -> Tuple[List[Instruction], List[ChangeInstruction]]:
|
44
44
|
if question_name not in self.question_names:
|
45
|
-
|
45
|
+
from .exceptions import InstructionCollectionError
|
46
|
+
raise InstructionCollectionError(
|
46
47
|
f"Question name not found in the list of questions: got '{question_name}'; list is {self.question_names}"
|
47
48
|
)
|
48
|
-
instructions
|
49
|
+
instructions: List[Instruction] = []
|
50
|
+
changes: List[ChangeInstruction] = []
|
49
51
|
|
50
52
|
index = self.question_index(question_name)
|
51
53
|
for instruction in self.instruction_names_to_instruction.values():
|
@@ -1,12 +1,13 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
2
1
|
from dataclasses import dataclass
|
3
2
|
|
4
3
|
|
4
|
+
from typing import Dict, List, Any
|
5
|
+
|
5
6
|
@dataclass
|
6
7
|
class SeparatedComponents:
|
7
|
-
true_questions:
|
8
|
-
instruction_names_to_instructions:
|
9
|
-
pseudo_indices:
|
8
|
+
true_questions: List[Any]
|
9
|
+
instruction_names_to_instructions: Dict[str, Any]
|
10
|
+
pseudo_indices: Dict[str, float]
|
10
11
|
|
11
12
|
|
12
13
|
class InstructionHandler:
|
@@ -14,7 +15,7 @@ class InstructionHandler:
|
|
14
15
|
self.survey = survey
|
15
16
|
|
16
17
|
@staticmethod
|
17
|
-
def separate_questions_and_instructions(questions_and_instructions: list) ->
|
18
|
+
def separate_questions_and_instructions(questions_and_instructions: list) -> SeparatedComponents:
|
18
19
|
"""
|
19
20
|
The 'pseudo_indices' attribute is a dictionary that maps question names to pseudo-indices
|
20
21
|
that are used to order questions and instructions in the survey.
|
@@ -46,18 +47,12 @@ class InstructionHandler:
|
|
46
47
|
['intro']
|
47
48
|
>>> [i.name for i in s._relevant_instructions(q3)]
|
48
49
|
[]
|
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
50
|
"""
|
56
51
|
from .instruction import Instruction
|
57
52
|
from .change_instruction import ChangeInstruction
|
58
|
-
from
|
53
|
+
from ..questions import QuestionBase
|
59
54
|
|
60
|
-
true_questions = []
|
55
|
+
true_questions: List[QuestionBase] = []
|
61
56
|
instruction_names_to_instructions = {}
|
62
57
|
|
63
58
|
num_change_instructions = 0
|
@@ -70,7 +65,8 @@ class InstructionHandler:
|
|
70
65
|
num_change_instructions += 1
|
71
66
|
for prior_instruction in entry.keep + entry.drop:
|
72
67
|
if prior_instruction not in instruction_names_to_instructions:
|
73
|
-
|
68
|
+
from .exceptions import InstructionValueError
|
69
|
+
raise InstructionValueError(
|
74
70
|
f"ChangeInstruction {entry.name} references instruction {prior_instruction} which does not exist."
|
75
71
|
)
|
76
72
|
instructions_run_length += 1
|
@@ -83,7 +79,8 @@ class InstructionHandler:
|
|
83
79
|
instructions_run_length = 0
|
84
80
|
true_questions.append(entry)
|
85
81
|
else:
|
86
|
-
|
82
|
+
from .exceptions import InstructionValueError
|
83
|
+
raise InstructionValueError(
|
87
84
|
f"Entry {repr(entry)} is not a QuestionBase or an Instruction."
|
88
85
|
)
|
89
86
|
|
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)
|
@@ -91,7 +92,7 @@ class InterviewExceptionEntry:
|
|
91
92
|
def code(self, run=True):
|
92
93
|
"""Return the code to reproduce the exception."""
|
93
94
|
lines = []
|
94
|
-
lines.append("from
|
95
|
+
lines.append("from .. import Question, Model, Scenario, Agent")
|
95
96
|
|
96
97
|
lines.append(f"q = {repr(self.invigilator.question)}")
|
97
98
|
lines.append(f"scenario = {repr(self.invigilator.scenario)}")
|
@@ -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:
|
@@ -363,7 +365,7 @@ class Interview:
|
|
363
365
|
bool: True if the interviews are equivalent, False otherwise
|
364
366
|
|
365
367
|
Examples:
|
366
|
-
>>> from
|
368
|
+
>>> from . import Interview
|
367
369
|
>>> i = Interview.example()
|
368
370
|
>>> d = i.to_dict()
|
369
371
|
>>> i2 = Interview.from_dict(d)
|
@@ -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 .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:
|
@@ -29,7 +29,7 @@ class InterviewTaskManager:
|
|
29
29
|
self, answer_func, token_estimator, model_buckets
|
30
30
|
) -> list[asyncio.Task]:
|
31
31
|
"""Create tasks for all questions with proper dependencies."""
|
32
|
-
tasks = []
|
32
|
+
tasks: list[asyncio.Task] = []
|
33
33
|
for question in self.survey.questions:
|
34
34
|
dependencies = self._get_task_dependencies(tasks, question)
|
35
35
|
task = self._create_single_task(
|
@@ -40,7 +40,7 @@ class InterviewTaskManager:
|
|
40
40
|
model_buckets=model_buckets,
|
41
41
|
)
|
42
42
|
tasks.append(task)
|
43
|
-
return
|
43
|
+
return tasks
|
44
44
|
|
45
45
|
def _get_task_dependencies(
|
46
46
|
self, existing_tasks: list[asyncio.Task], question: "QuestionBase"
|
@@ -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,8 +26,10 @@ class RequestTokenEstimator:
|
|
25
26
|
if isinstance(file, FileStore):
|
26
27
|
file_tokens += file.size * 0.25
|
27
28
|
else:
|
28
|
-
|
29
|
-
|
29
|
+
from .exceptions import InterviewTokenError
|
30
|
+
raise InterviewTokenError(f"Prompt is of type {type(prompt)}")
|
31
|
+
result: float = len(combined_text) / 4.0 + file_tokens
|
32
|
+
return result
|
30
33
|
|
31
34
|
|
32
35
|
|
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, Union, Optional
|
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
|
@@ -44,7 +43,7 @@ class InterviewStatistic(UserDict):
|
|
44
43
|
value: float,
|
45
44
|
digits: int = 0,
|
46
45
|
units: str = "",
|
47
|
-
pretty_name: str = None,
|
46
|
+
pretty_name: Optional[str] = None,
|
48
47
|
):
|
49
48
|
"""Create a new InterviewStatistic object."""
|
50
49
|
self.name = name
|
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
|
+
]
|