edsl 0.1.50__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/__version__.py +1 -1
- edsl/base/base_exception.py +2 -2
- edsl/buckets/bucket_collection.py +1 -1
- edsl/buckets/exceptions.py +32 -0
- edsl/buckets/token_bucket_api.py +26 -10
- edsl/caching/cache.py +5 -2
- edsl/caching/remote_cache_sync.py +5 -5
- edsl/caching/sql_dict.py +12 -11
- edsl/config/__init__.py +1 -1
- edsl/config/config_class.py +4 -2
- edsl/conversation/Conversation.py +7 -4
- edsl/conversation/car_buying.py +1 -3
- edsl/conversation/mug_negotiation.py +2 -6
- edsl/coop/__init__.py +11 -8
- edsl/coop/coop.py +13 -13
- edsl/coop/coop_functions.py +1 -1
- edsl/coop/ep_key_handling.py +1 -1
- edsl/coop/price_fetcher.py +2 -2
- edsl/coop/utils.py +2 -2
- edsl/dataset/dataset.py +144 -63
- edsl/dataset/dataset_operations_mixin.py +14 -6
- edsl/dataset/dataset_tree.py +3 -3
- edsl/dataset/display/table_renderers.py +6 -3
- edsl/dataset/file_exports.py +4 -4
- edsl/dataset/r/ggplot.py +3 -3
- edsl/inference_services/available_model_fetcher.py +2 -2
- edsl/inference_services/data_structures.py +5 -5
- edsl/inference_services/inference_service_abc.py +1 -1
- edsl/inference_services/inference_services_collection.py +1 -1
- edsl/inference_services/service_availability.py +3 -3
- edsl/inference_services/services/azure_ai.py +3 -3
- edsl/inference_services/services/google_service.py +1 -1
- edsl/inference_services/services/test_service.py +1 -1
- edsl/instructions/change_instruction.py +5 -4
- edsl/instructions/instruction.py +1 -0
- edsl/instructions/instruction_collection.py +5 -4
- edsl/instructions/instruction_handler.py +10 -8
- edsl/interviews/exception_tracking.py +1 -1
- edsl/interviews/interview.py +1 -1
- edsl/interviews/interview_status_dictionary.py +1 -1
- edsl/interviews/interview_task_manager.py +2 -2
- edsl/interviews/request_token_estimator.py +3 -2
- edsl/interviews/statistics.py +2 -2
- edsl/invigilators/invigilators.py +2 -2
- edsl/jobs/__init__.py +39 -2
- edsl/jobs/async_interview_runner.py +1 -1
- edsl/jobs/check_survey_scenario_compatibility.py +5 -5
- edsl/jobs/data_structures.py +2 -2
- edsl/jobs/jobs.py +2 -2
- edsl/jobs/jobs_checks.py +5 -5
- edsl/jobs/jobs_component_constructor.py +2 -2
- edsl/jobs/jobs_pricing_estimation.py +1 -1
- edsl/jobs/jobs_runner_asyncio.py +2 -2
- edsl/jobs/remote_inference.py +1 -1
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/language_models/language_model.py +5 -1
- 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 -1
- edsl/prompts/__init__.py +23 -2
- edsl/prompts/prompt.py +1 -1
- edsl/questions/__init__.py +4 -4
- edsl/questions/answer_validator_mixin.py +0 -5
- edsl/questions/compose_questions.py +2 -2
- edsl/questions/descriptors.py +1 -1
- edsl/questions/question_base.py +32 -3
- edsl/questions/question_base_prompts_mixin.py +4 -4
- edsl/questions/question_budget.py +503 -102
- edsl/questions/question_check_box.py +658 -156
- edsl/questions/question_dict.py +176 -2
- edsl/questions/question_extract.py +401 -61
- edsl/questions/question_free_text.py +77 -9
- edsl/questions/question_functional.py +118 -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 +246 -26
- edsl/questions/question_matrix.py +586 -73
- edsl/questions/question_multiple_choice.py +213 -47
- edsl/questions/question_numerical.py +360 -29
- edsl/questions/question_rank.py +401 -124
- edsl/questions/question_registry.py +3 -3
- 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 -1
- edsl/questions/response_validator_abc.py +6 -2
- edsl/questions/response_validator_factory.py +10 -12
- edsl/results/report.py +1 -1
- edsl/results/result.py +7 -4
- edsl/results/results.py +471 -271
- edsl/results/results_selector.py +2 -2
- edsl/scenarios/construct_download_link.py +3 -3
- edsl/scenarios/scenario.py +1 -2
- edsl/scenarios/scenario_list.py +41 -23
- edsl/surveys/survey_css.py +3 -3
- edsl/surveys/survey_simulator.py +2 -1
- edsl/tasks/__init__.py +22 -2
- edsl/tasks/exceptions.py +72 -0
- edsl/tasks/task_history.py +3 -3
- edsl/tokens/__init__.py +27 -1
- edsl/tokens/exceptions.py +37 -0
- edsl/tokens/interview_token_usage.py +3 -2
- edsl/tokens/token_usage.py +4 -3
- {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/METADATA +1 -1
- {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/RECORD +108 -106
- edsl/questions/derived/__init__.py +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/LICENSE +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/WHEEL +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/entry_points.txt +0 -0
edsl/results/results_selector.py
CHANGED
@@ -106,7 +106,7 @@ class Selector:
|
|
106
106
|
new_data = self._fetch_data(to_fetch)
|
107
107
|
except ResultsColumnNotFoundError as e:
|
108
108
|
# Check is_notebook with explicit import to ensure mock works
|
109
|
-
from
|
109
|
+
from ..utilities import is_notebook as is_notebook_check
|
110
110
|
if is_notebook_check():
|
111
111
|
print("Error:", e, file=sys.stderr)
|
112
112
|
return None
|
@@ -114,7 +114,7 @@ class Selector:
|
|
114
114
|
raise e
|
115
115
|
|
116
116
|
# Import Dataset here to avoid circular import issues
|
117
|
-
from
|
117
|
+
from ..dataset import Dataset
|
118
118
|
return Dataset(new_data)
|
119
119
|
|
120
120
|
def _normalize_columns(self, columns: Union[str, List[str]]) -> Tuple[str, ...]:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import os
|
4
|
-
from typing import TYPE_CHECKING, Optional
|
4
|
+
from typing import TYPE_CHECKING, Optional, List
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
7
7
|
from ..display import HTML
|
@@ -104,8 +104,8 @@ class ConstructDownloadLink:
|
|
104
104
|
|
105
105
|
def create_multiple_links(
|
106
106
|
self,
|
107
|
-
files:
|
108
|
-
custom_filenames: Optional[
|
107
|
+
files: List["FileStore"],
|
108
|
+
custom_filenames: Optional[List[Optional[str]]] = None,
|
109
109
|
style: Optional[dict] = None,
|
110
110
|
) -> HTML:
|
111
111
|
"""Create multiple download links in a horizontal layout.
|
edsl/scenarios/scenario.py
CHANGED
@@ -96,8 +96,7 @@ class Scenario(Base, UserDict):
|
|
96
96
|
data = dict(data)
|
97
97
|
except Exception as e:
|
98
98
|
raise ScenarioError(
|
99
|
-
f"You must pass in a dictionary to initialize a Scenario. You passed in {data}",
|
100
|
-
"Exception message:" + str(e),
|
99
|
+
f"You must pass in a dictionary to initialize a Scenario. You passed in {data}" + "Exception message:" + str(e),
|
101
100
|
)
|
102
101
|
|
103
102
|
super().__init__()
|
edsl/scenarios/scenario_list.py
CHANGED
@@ -31,7 +31,6 @@ import warnings
|
|
31
31
|
import csv
|
32
32
|
import random
|
33
33
|
import os
|
34
|
-
import glob
|
35
34
|
from io import StringIO
|
36
35
|
import inspect
|
37
36
|
from collections import UserList, defaultdict
|
@@ -488,7 +487,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
488
487
|
if isinstance(other, Scenario):
|
489
488
|
other = ScenarioList([other])
|
490
489
|
elif not isinstance(other, ScenarioList):
|
491
|
-
|
490
|
+
from .exceptions import TypeScenarioError
|
491
|
+
raise TypeScenarioError(f"Cannot multiply ScenarioList with {type(other)}")
|
492
492
|
|
493
493
|
new_sl = []
|
494
494
|
for s1, s2 in list(product(self, other)):
|
@@ -599,7 +599,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
599
599
|
# Convert to a set (removes duplicates)
|
600
600
|
new_scenario[field_name] = set(values)
|
601
601
|
else:
|
602
|
-
|
602
|
+
from .exceptions import ValueScenarioError
|
603
|
+
raise ValueScenarioError(f"Invalid output_type: {output_type}. Must be 'string', 'list', or 'set'.")
|
603
604
|
|
604
605
|
new_scenarios.append(new_scenario)
|
605
606
|
|
@@ -895,11 +896,13 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
895
896
|
cls,
|
896
897
|
path: Optional[str] = None,
|
897
898
|
recursive: bool = False,
|
899
|
+
key_name: str = "content",
|
898
900
|
) -> "ScenarioList":
|
899
|
-
"""Create a ScenarioList of
|
901
|
+
"""Create a ScenarioList of Scenario objects from files in a directory.
|
900
902
|
|
901
|
-
This method scans a directory and creates a
|
902
|
-
|
903
|
+
This method scans a directory and creates a Scenario object for each file found,
|
904
|
+
where each Scenario contains a FileStore object under the specified key.
|
905
|
+
Optionally filters files based on a wildcard pattern. If no path is provided,
|
903
906
|
the current working directory is used.
|
904
907
|
|
905
908
|
Args:
|
@@ -910,25 +913,27 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
910
913
|
- "/path/to/directory/*.py" - scans only Python files in the directory
|
911
914
|
- "*.txt" - scans only text files in the current working directory
|
912
915
|
recursive: Whether to scan subdirectories recursively. Defaults to False.
|
916
|
+
key_name: The key to use for the FileStore object in each Scenario. Defaults to "content".
|
913
917
|
|
914
918
|
Returns:
|
915
|
-
A ScenarioList containing
|
919
|
+
A ScenarioList containing Scenario objects for all matching files, where each Scenario
|
920
|
+
has a FileStore object under the specified key.
|
916
921
|
|
917
922
|
Raises:
|
918
923
|
FileNotFoundError: If the specified directory does not exist.
|
919
924
|
|
920
925
|
Examples:
|
921
|
-
# Get all files in the current directory
|
926
|
+
# Get all files in the current directory with default key "content"
|
922
927
|
sl = ScenarioList.from_directory()
|
923
928
|
|
924
|
-
# Get all Python files in a specific directory
|
925
|
-
sl = ScenarioList.from_directory('*.py')
|
929
|
+
# Get all Python files in a specific directory with custom key "python_file"
|
930
|
+
sl = ScenarioList.from_directory('*.py', key_name="python_file")
|
926
931
|
|
927
932
|
# Get all image files in the current directory
|
928
|
-
sl = ScenarioList.from_directory('*.png')
|
933
|
+
sl = ScenarioList.from_directory('*.png', key_name="image")
|
929
934
|
|
930
935
|
# Get all files recursively including subdirectories
|
931
|
-
sl = ScenarioList.from_directory(recursive=True)
|
936
|
+
sl = ScenarioList.from_directory(recursive=True, key_name="document")
|
932
937
|
"""
|
933
938
|
# Handle default case - use current directory
|
934
939
|
if path is None:
|
@@ -973,7 +978,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
973
978
|
|
974
979
|
# Ensure directory exists
|
975
980
|
if not os.path.isdir(directory_path):
|
976
|
-
|
981
|
+
from .exceptions import FileNotFoundScenarioError
|
982
|
+
raise FileNotFoundScenarioError(f"Directory not found: {directory_path}")
|
977
983
|
|
978
984
|
# Create a DirectoryScanner for the directory
|
979
985
|
scanner = DirectoryScanner(directory_path)
|
@@ -1001,7 +1007,10 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1001
1007
|
example_suffix=example_suffix
|
1002
1008
|
)
|
1003
1009
|
|
1004
|
-
|
1010
|
+
# Convert FileStore objects to Scenario objects with the specified key
|
1011
|
+
scenarios = [Scenario({key_name: file_store}) for file_store in file_stores]
|
1012
|
+
|
1013
|
+
return cls(scenarios)
|
1005
1014
|
|
1006
1015
|
@classmethod
|
1007
1016
|
def from_list(
|
@@ -1262,7 +1271,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1262
1271
|
import sqlite3
|
1263
1272
|
|
1264
1273
|
if table is None and sql_query is None:
|
1265
|
-
|
1274
|
+
from .exceptions import ValueScenarioError
|
1275
|
+
raise ValueScenarioError("Either table or sql_query must be provided")
|
1266
1276
|
|
1267
1277
|
try:
|
1268
1278
|
with sqlite3.connect(filepath) as conn:
|
@@ -1328,7 +1338,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1328
1338
|
if "/edit" in url:
|
1329
1339
|
doc_id = url.split("/d/")[1].split("/edit")[0]
|
1330
1340
|
else:
|
1331
|
-
|
1341
|
+
from .exceptions import ValueScenarioError
|
1342
|
+
raise ValueScenarioError("Invalid Google Doc URL format.")
|
1332
1343
|
|
1333
1344
|
export_url = f"https://docs.google.com/document/d/{doc_id}/export?format=docx"
|
1334
1345
|
|
@@ -1532,7 +1543,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1532
1543
|
print("The Excel file contains multiple sheets:")
|
1533
1544
|
for name in all_sheets.keys():
|
1534
1545
|
print(f"- {name}")
|
1535
|
-
|
1546
|
+
from .exceptions import ValueScenarioError
|
1547
|
+
raise ValueScenarioError("Please provide a sheet name to load data from.")
|
1536
1548
|
else:
|
1537
1549
|
# If there is only one sheet, use it
|
1538
1550
|
sheet_name = list(all_sheets.keys())[0]
|
@@ -1587,7 +1599,8 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1587
1599
|
if "/edit" in url:
|
1588
1600
|
sheet_id = url.split("/d/")[1].split("/edit")[0]
|
1589
1601
|
else:
|
1590
|
-
|
1602
|
+
from .exceptions import ValueScenarioError
|
1603
|
+
raise ValueScenarioError("Invalid Google Sheet URL format.")
|
1591
1604
|
|
1592
1605
|
export_url = (
|
1593
1606
|
f"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=xlsx"
|
@@ -1673,14 +1686,16 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1673
1686
|
file_obj = None
|
1674
1687
|
|
1675
1688
|
if file_obj is None:
|
1676
|
-
|
1689
|
+
from .exceptions import ValueScenarioError
|
1690
|
+
raise ValueScenarioError(f"Could not decode file {source} with any of the attempted encodings. Original error: {last_exception}")
|
1677
1691
|
|
1678
1692
|
reader = csv.reader(file_obj, delimiter=delimiter)
|
1679
1693
|
try:
|
1680
1694
|
header = next(reader)
|
1681
1695
|
observations = [Scenario(dict(zip(header, row))) for row in reader]
|
1682
1696
|
except StopIteration:
|
1683
|
-
|
1697
|
+
from .exceptions import ValueScenarioError
|
1698
|
+
raise ValueScenarioError(f"File {source} appears to be empty or has an invalid format")
|
1684
1699
|
|
1685
1700
|
finally:
|
1686
1701
|
if file_obj:
|
@@ -1996,13 +2011,16 @@ class ScenarioList(Base, UserList, ScenarioListOperationsMixin):
|
|
1996
2011
|
import string
|
1997
2012
|
|
1998
2013
|
if num_options < 2:
|
1999
|
-
|
2014
|
+
from .exceptions import ValueScenarioError
|
2015
|
+
raise ValueScenarioError("num_options must be at least 2")
|
2000
2016
|
|
2001
2017
|
if num_options > len(self):
|
2002
|
-
|
2018
|
+
from .exceptions import ValueScenarioError
|
2019
|
+
raise ValueScenarioError(f"num_options ({num_options}) cannot exceed the number of scenarios ({len(self)})")
|
2003
2020
|
|
2004
2021
|
if use_alphabet and num_options > 26:
|
2005
|
-
|
2022
|
+
from .exceptions import ValueScenarioError
|
2023
|
+
raise ValueScenarioError("When using alphabet labels, num_options cannot exceed 26 (the number of letters in the English alphabet)")
|
2006
2024
|
|
2007
2025
|
# Convert each scenario to a dictionary
|
2008
2026
|
scenario_dicts = [scenario.to_dict(add_edsl_version=False) for scenario in self]
|
edsl/surveys/survey_css.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from typing import Optional
|
2
|
-
from
|
2
|
+
from ..utilities.remove_edsl_version import remove_edsl_version
|
3
3
|
|
4
4
|
|
5
5
|
class CSSRuleMeta(type):
|
@@ -73,7 +73,7 @@ class CSSRule(metaclass=CSSRuleMeta):
|
|
73
73
|
d = {"selector": self.selector, "properties": self.properties}
|
74
74
|
|
75
75
|
if add_esl_version:
|
76
|
-
from
|
76
|
+
from .. import __version__
|
77
77
|
|
78
78
|
d["edsl_version"] = __version__
|
79
79
|
d["edsl_class_name"] = self.__class__.__name__
|
@@ -233,7 +233,7 @@ class SurveyCSS:
|
|
233
233
|
"""
|
234
234
|
d = {"rules": [rule.to_dict() for rule in self.rules.values()]}
|
235
235
|
if add_edsl_version:
|
236
|
-
from
|
236
|
+
from .. import __version__
|
237
237
|
|
238
238
|
d["edsl_version"] = __version__
|
239
239
|
d["edsl_class_name"] = self.__class__.__name__
|
edsl/surveys/survey_simulator.py
CHANGED
@@ -58,7 +58,8 @@ class Simulator:
|
|
58
58
|
|
59
59
|
if num_passes > 100:
|
60
60
|
print("Too many passes.")
|
61
|
-
|
61
|
+
from .exceptions import SurveyError
|
62
|
+
raise SurveyError("Too many passes.")
|
62
63
|
return self.survey.answers
|
63
64
|
|
64
65
|
def create_agent(self) -> "Agent":
|
edsl/tasks/__init__.py
CHANGED
@@ -26,9 +26,29 @@ For most users, this module works behind the scenes, but understanding it can
|
|
26
26
|
be helpful when debugging or optimizing complex EDSL workflows.
|
27
27
|
"""
|
28
28
|
|
29
|
-
__all__ = [
|
29
|
+
__all__ = [
|
30
|
+
'TaskHistory',
|
31
|
+
'QuestionTaskCreator',
|
32
|
+
'TaskCreators',
|
33
|
+
'TaskStatus',
|
34
|
+
'TaskStatusDescriptor',
|
35
|
+
'TaskError',
|
36
|
+
'TaskStatusError',
|
37
|
+
'TaskExecutionError',
|
38
|
+
'TaskDependencyError',
|
39
|
+
'TaskResourceError',
|
40
|
+
'TaskHistoryError'
|
41
|
+
]
|
30
42
|
|
31
43
|
from .task_history import TaskHistory
|
32
44
|
from .question_task_creator import QuestionTaskCreator
|
33
45
|
from .task_creators import TaskCreators
|
34
|
-
from .task_status_enum import TaskStatus, TaskStatusDescriptor
|
46
|
+
from .task_status_enum import TaskStatus, TaskStatusDescriptor
|
47
|
+
from .exceptions import (
|
48
|
+
TaskError,
|
49
|
+
TaskStatusError,
|
50
|
+
TaskExecutionError,
|
51
|
+
TaskDependencyError,
|
52
|
+
TaskResourceError,
|
53
|
+
TaskHistoryError
|
54
|
+
)
|
edsl/tasks/exceptions.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
"""
|
2
|
+
This module defines the exception hierarchy for the tasks module.
|
3
|
+
|
4
|
+
All exceptions related to task creation, execution, and management are defined here.
|
5
|
+
These exceptions provide detailed error information for debugging and error reporting.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from ..base import BaseException
|
9
|
+
|
10
|
+
|
11
|
+
class TaskError(BaseException):
|
12
|
+
"""
|
13
|
+
Base exception for all tasks-related errors.
|
14
|
+
|
15
|
+
This is the parent class for all exceptions raised within the tasks module.
|
16
|
+
It inherits from BaseException to ensure proper error tracking and reporting.
|
17
|
+
"""
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class TaskStatusError(TaskError):
|
22
|
+
"""
|
23
|
+
Raised when a task encounters an invalid status transition.
|
24
|
+
|
25
|
+
This exception is raised when a task attempts to transition to an invalid state
|
26
|
+
based on its current state, such as trying to set a completed task to running.
|
27
|
+
|
28
|
+
Attributes:
|
29
|
+
current_status: The current status of the task
|
30
|
+
attempted_status: The status that could not be set
|
31
|
+
"""
|
32
|
+
pass
|
33
|
+
|
34
|
+
|
35
|
+
class TaskExecutionError(TaskError):
|
36
|
+
"""
|
37
|
+
Raised when a task encounters an error during execution.
|
38
|
+
|
39
|
+
This is a general exception for errors that occur while a task is running,
|
40
|
+
not specific to dependency resolution or resource allocation.
|
41
|
+
"""
|
42
|
+
pass
|
43
|
+
|
44
|
+
|
45
|
+
class TaskDependencyError(TaskError):
|
46
|
+
"""
|
47
|
+
Raised when there is an issue with task dependencies.
|
48
|
+
|
49
|
+
This exception is raised for dependency-related issues, such as circular
|
50
|
+
dependencies or errors in dependent tasks.
|
51
|
+
"""
|
52
|
+
pass
|
53
|
+
|
54
|
+
|
55
|
+
class TaskResourceError(TaskError):
|
56
|
+
"""
|
57
|
+
Raised when a task cannot acquire necessary resources.
|
58
|
+
|
59
|
+
This exception is used when a task cannot obtain required resources
|
60
|
+
such as tokens or request capacity, beyond normal waiting situations.
|
61
|
+
"""
|
62
|
+
pass
|
63
|
+
|
64
|
+
|
65
|
+
class TaskHistoryError(TaskError):
|
66
|
+
"""
|
67
|
+
Raised for errors related to task history operations.
|
68
|
+
|
69
|
+
This exception covers issues with recording, accessing, or analyzing
|
70
|
+
task execution history and logs.
|
71
|
+
"""
|
72
|
+
pass
|
edsl/tasks/task_history.py
CHANGED
@@ -146,7 +146,7 @@ class TaskHistory(RepresentationMixin):
|
|
146
146
|
"include_traceback": self.include_traceback,
|
147
147
|
}
|
148
148
|
if add_edsl_version:
|
149
|
-
from
|
149
|
+
from .. import __version__
|
150
150
|
|
151
151
|
d["edsl_version"] = __version__
|
152
152
|
d["edsl_class_name"] = "TaskHistory"
|
@@ -406,7 +406,7 @@ class TaskHistory(RepresentationMixin):
|
|
406
406
|
models_used = set([i.model.model for index, i in self._interviews.items()])
|
407
407
|
|
408
408
|
from jinja2 import Environment
|
409
|
-
from
|
409
|
+
from ..utilities import TemplateLoader
|
410
410
|
|
411
411
|
env = Environment(loader=TemplateLoader("edsl", "templates/error_reporting"))
|
412
412
|
|
@@ -465,7 +465,7 @@ class TaskHistory(RepresentationMixin):
|
|
465
465
|
"""
|
466
466
|
from IPython.display import display, HTML
|
467
467
|
import os
|
468
|
-
from
|
468
|
+
from ..utilities.utilities import is_notebook
|
469
469
|
|
470
470
|
output = self.generate_html_report(css)
|
471
471
|
|
edsl/tokens/__init__.py
CHANGED
@@ -1,4 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
The tokens module provides functionality for tracking and analyzing token usage in EDSL.
|
3
|
+
|
4
|
+
This module implements classes for tracking and reporting token usage across various
|
5
|
+
components of EDSL, particularly for language model calls. It supports aggregation,
|
6
|
+
cost calculation, and reporting of token usage metrics.
|
7
|
+
|
8
|
+
Key components:
|
9
|
+
1. TokenUsage - Tracks prompt and completion tokens for a single operation
|
10
|
+
2. InterviewTokenUsage - Aggregates token usage across an entire interview
|
11
|
+
3. Exception classes for handling token-related errors
|
12
|
+
|
13
|
+
The token tracking system helps with:
|
14
|
+
- Cost estimation and billing
|
15
|
+
- Resource utilization analysis
|
16
|
+
- Cache effectiveness measurement
|
17
|
+
- API quota management
|
18
|
+
"""
|
19
|
+
|
1
20
|
from .token_usage import TokenUsage
|
2
21
|
from .interview_token_usage import InterviewTokenUsage
|
22
|
+
from .exceptions import TokenError, TokenUsageError, TokenCostError
|
3
23
|
|
4
|
-
__all__ = [
|
24
|
+
__all__ = [
|
25
|
+
"TokenUsage",
|
26
|
+
"InterviewTokenUsage",
|
27
|
+
"TokenError",
|
28
|
+
"TokenUsageError",
|
29
|
+
"TokenCostError"
|
30
|
+
]
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"""
|
2
|
+
This module defines the exception hierarchy for the tokens module.
|
3
|
+
|
4
|
+
All exceptions related to token usage tracking and cost calculations are defined here.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from ..base import BaseException
|
8
|
+
|
9
|
+
|
10
|
+
class TokenError(BaseException):
|
11
|
+
"""
|
12
|
+
Base exception for all token-related errors.
|
13
|
+
|
14
|
+
This is the parent class for all exceptions raised within the tokens module.
|
15
|
+
It inherits from BaseException to ensure proper error tracking and reporting.
|
16
|
+
"""
|
17
|
+
pass
|
18
|
+
|
19
|
+
|
20
|
+
class TokenUsageError(TokenError):
|
21
|
+
"""
|
22
|
+
Raised when there is an error in token usage operations.
|
23
|
+
|
24
|
+
This exception is raised for issues related to token usage tracking,
|
25
|
+
such as invalid token counts or incompatible token usage types.
|
26
|
+
"""
|
27
|
+
pass
|
28
|
+
|
29
|
+
|
30
|
+
class TokenCostError(TokenError):
|
31
|
+
"""
|
32
|
+
Raised when there is an error in token cost calculations.
|
33
|
+
|
34
|
+
This exception is used for issues with cost calculations, such as
|
35
|
+
missing or invalid pricing information.
|
36
|
+
"""
|
37
|
+
pass
|
@@ -1,7 +1,8 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
3
|
from .token_usage import TokenUsage
|
4
|
-
from
|
4
|
+
from ..enums import TokenPricing
|
5
|
+
from .exceptions import TokenUsageError
|
5
6
|
|
6
7
|
class InterviewTokenUsage:
|
7
8
|
"""A class to represent the token usage of an interview."""
|
@@ -24,7 +25,7 @@ class InterviewTokenUsage:
|
|
24
25
|
>>> usage3 = usage1 + usage2
|
25
26
|
"""
|
26
27
|
if not isinstance(other, InterviewTokenUsage):
|
27
|
-
raise
|
28
|
+
raise TokenUsageError(f"Can't add {type(other)} to InterviewTokenSummary")
|
28
29
|
return InterviewTokenUsage(
|
29
30
|
new_token_usage=self.new_token_usage + other.new_token_usage,
|
30
31
|
cached_token_usage=self.cached_token_usage + other.cached_token_usage,
|
edsl/tokens/token_usage.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
1
|
+
from ..enums import TokenPricing
|
2
|
+
from .exceptions import TokenUsageError, TokenCostError
|
2
3
|
|
3
4
|
|
4
5
|
class TokenUsage:
|
@@ -15,9 +16,9 @@ class TokenUsage:
|
|
15
16
|
|
16
17
|
def __add__(self, other):
|
17
18
|
if not isinstance(other, TokenUsage):
|
18
|
-
raise
|
19
|
+
raise TokenUsageError(f"Can't add {type(other)} to InterviewTokenUsage")
|
19
20
|
if self.from_cache != other.from_cache:
|
20
|
-
raise
|
21
|
+
raise TokenUsageError("Can't add token usages from different sources")
|
21
22
|
return TokenUsage(
|
22
23
|
from_cache=self.from_cache,
|
23
24
|
prompt_tokens=self.prompt_tokens + other.prompt_tokens,
|