edsl 0.1.50__py3-none-any.whl → 0.1.52__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 +45 -34
- 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 +9 -5
- edsl/conversation/car_buying.py +1 -3
- edsl/conversation/mug_negotiation.py +2 -6
- edsl/coop/__init__.py +11 -8
- edsl/coop/coop.py +15 -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/answering_function.py +20 -21
- edsl/interviews/exception_tracking.py +3 -2
- edsl/interviews/interview.py +1 -1
- edsl/interviews/interview_status_dictionary.py +1 -1
- edsl/interviews/interview_task_manager.py +7 -4
- edsl/interviews/request_token_estimator.py +3 -2
- edsl/interviews/statistics.py +2 -2
- edsl/invigilators/invigilators.py +34 -6
- 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/html_table_job_logger.py +494 -257
- 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/jobs_status_enums.py +1 -0
- edsl/jobs/remote_inference.py +47 -13
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/language_models/language_model.py +151 -145
- 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 +500 -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 +48 -11
- edsl/templates/error_reporting/base.html +37 -4
- edsl/templates/error_reporting/exceptions_table.html +105 -33
- edsl/templates/error_reporting/interview_details.html +130 -126
- edsl/templates/error_reporting/overview.html +21 -25
- edsl/templates/error_reporting/report.css +215 -46
- edsl/templates/error_reporting/report.js +122 -20
- 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.52.dist-info}/METADATA +1 -1
- {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/RECORD +118 -116
- edsl/questions/derived/__init__.py +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/LICENSE +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/WHEEL +0 -0
- {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/entry_points.txt +0 -0
edsl/notebooks/__init__.py
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
The notebooks module provides tools for working with Jupyter notebooks.
|
3
|
+
|
4
|
+
It includes classes for notebook creation, manipulation, and conversion
|
5
|
+
to other formats such as LaTeX.
|
6
|
+
"""
|
7
|
+
|
1
8
|
from .notebook import Notebook
|
9
|
+
from .notebook_to_latex import NotebookToLaTeX
|
10
|
+
from .exceptions import (
|
11
|
+
NotebookError,
|
12
|
+
NotebookValueError,
|
13
|
+
NotebookFormatError,
|
14
|
+
NotebookConversionError,
|
15
|
+
NotebookEnvironmentError,
|
16
|
+
)
|
2
17
|
|
3
|
-
__all__ = [
|
18
|
+
__all__ = [
|
19
|
+
"Notebook",
|
20
|
+
"NotebookToLaTeX",
|
21
|
+
"NotebookError",
|
22
|
+
"NotebookValueError",
|
23
|
+
"NotebookFormatError",
|
24
|
+
"NotebookConversionError",
|
25
|
+
"NotebookEnvironmentError",
|
26
|
+
]
|
@@ -0,0 +1,82 @@
|
|
1
|
+
"""
|
2
|
+
Custom exceptions for the notebooks module.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from ..base import BaseException
|
6
|
+
|
7
|
+
|
8
|
+
class NotebookError(BaseException):
|
9
|
+
"""
|
10
|
+
Base exception class for all notebook-related errors.
|
11
|
+
|
12
|
+
This is the parent class for all exceptions related to notebook
|
13
|
+
operations, including creation, validation, and conversion.
|
14
|
+
"""
|
15
|
+
relevant_doc = "https://docs.expectedparrot.com/"
|
16
|
+
|
17
|
+
|
18
|
+
class NotebookValueError(NotebookError):
|
19
|
+
"""
|
20
|
+
Exception raised when an invalid value is provided to a notebook method.
|
21
|
+
|
22
|
+
This exception occurs when attempting to create or modify a notebook
|
23
|
+
with invalid values, such as:
|
24
|
+
- Invalid data format
|
25
|
+
- Incompatible notebook contents
|
26
|
+
|
27
|
+
Examples:
|
28
|
+
```python
|
29
|
+
# Attempting to create a notebook with invalid data
|
30
|
+
notebook = Notebook(data=invalid_data) # Raises NotebookValueError
|
31
|
+
```
|
32
|
+
"""
|
33
|
+
relevant_doc = "https://docs.expectedparrot.com/"
|
34
|
+
|
35
|
+
|
36
|
+
class NotebookFormatError(NotebookError):
|
37
|
+
"""
|
38
|
+
Exception raised when a notebook's format is invalid.
|
39
|
+
|
40
|
+
This exception occurs when the notebook structure does not conform
|
41
|
+
to the expected Jupyter Notebook format.
|
42
|
+
|
43
|
+
Examples:
|
44
|
+
```python
|
45
|
+
# Attempting to load a notebook with invalid format
|
46
|
+
notebook = Notebook.from_dict(invalid_dict) # Raises NotebookFormatError
|
47
|
+
```
|
48
|
+
"""
|
49
|
+
relevant_doc = "https://docs.expectedparrot.com/"
|
50
|
+
|
51
|
+
|
52
|
+
class NotebookConversionError(NotebookError):
|
53
|
+
"""
|
54
|
+
Exception raised when a notebook conversion fails.
|
55
|
+
|
56
|
+
This exception occurs when attempting to convert a notebook to another
|
57
|
+
format (like LaTeX) and the conversion process fails.
|
58
|
+
|
59
|
+
Examples:
|
60
|
+
```python
|
61
|
+
# Attempting to convert a notebook to LaTeX with invalid options
|
62
|
+
notebook.to_latex(invalid_options) # Raises NotebookConversionError
|
63
|
+
```
|
64
|
+
"""
|
65
|
+
relevant_doc = "https://docs.expectedparrot.com/"
|
66
|
+
|
67
|
+
|
68
|
+
class NotebookEnvironmentError(NotebookError):
|
69
|
+
"""
|
70
|
+
Exception raised when the notebook environment is not supportable.
|
71
|
+
|
72
|
+
This exception occurs when attempting to create a notebook in an
|
73
|
+
environment that doesn't provide required context, such as when
|
74
|
+
trying to create a notebook from within itself outside of VS Code.
|
75
|
+
|
76
|
+
Examples:
|
77
|
+
```python
|
78
|
+
# Attempting to create a notebook from within itself in an unsupported IDE
|
79
|
+
notebook = Notebook() # Raises NotebookEnvironmentError
|
80
|
+
```
|
81
|
+
"""
|
82
|
+
relevant_doc = "https://docs.expectedparrot.com/"
|
edsl/notebooks/notebook.py
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
import json
|
5
|
-
from typing import Dict, List, Optional
|
5
|
+
from typing import Dict, List, Optional, TYPE_CHECKING
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from rich.table import Table
|
6
9
|
from uuid import uuid4
|
7
10
|
|
8
11
|
from ..base import Base
|
@@ -47,7 +50,8 @@ class Notebook(Base):
|
|
47
50
|
self.data = json.loads(json.dumps(data))
|
48
51
|
else:
|
49
52
|
# TODO: Support for IDEs other than VSCode
|
50
|
-
|
53
|
+
from .exceptions import NotebookEnvironmentError
|
54
|
+
raise NotebookEnvironmentError(
|
51
55
|
"Cannot create a notebook from within itself in this development environment"
|
52
56
|
)
|
53
57
|
|
@@ -251,7 +255,7 @@ class Notebook(Base):
|
|
251
255
|
|
252
256
|
:param filename: Name of the output folder and main tex file (without extension)
|
253
257
|
"""
|
254
|
-
from .
|
258
|
+
from .notebook_to_latex import NotebookToLaTeX
|
255
259
|
|
256
260
|
NotebookToLaTeX(self).convert(filename)
|
257
261
|
|
edsl/prompts/__init__.py
CHANGED
@@ -1,4 +1,25 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
The prompts module provides tools for creating and managing prompts.
|
3
|
+
|
4
|
+
It includes classes for template-based prompts with variable substitution,
|
5
|
+
prompt rendering, and component management for language model interactions.
|
6
|
+
"""
|
2
7
|
from .prompt import Prompt
|
8
|
+
from .exceptions import (
|
9
|
+
PromptError,
|
10
|
+
TemplateRenderError,
|
11
|
+
PromptBadQuestionTypeError,
|
12
|
+
PromptBadLanguageModelTypeError,
|
13
|
+
PromptValueError,
|
14
|
+
PromptImplementationError
|
15
|
+
)
|
3
16
|
|
4
|
-
__all__ = [
|
17
|
+
__all__ = [
|
18
|
+
"Prompt",
|
19
|
+
"PromptError",
|
20
|
+
"TemplateRenderError",
|
21
|
+
"PromptBadQuestionTypeError",
|
22
|
+
"PromptBadLanguageModelTypeError",
|
23
|
+
"PromptValueError",
|
24
|
+
"PromptImplementationError"
|
25
|
+
]
|
edsl/prompts/prompt.py
CHANGED
@@ -145,7 +145,7 @@ class Prompt(PersistenceMixin, RepresentationMixin):
|
|
145
145
|
if path_to_folder is None:
|
146
146
|
from importlib import resources
|
147
147
|
|
148
|
-
path_to_folder = resources.path("
|
148
|
+
path_to_folder = resources.path("..questions", "prompt_templates")
|
149
149
|
|
150
150
|
try:
|
151
151
|
folder_path = Path(path_to_folder)
|
edsl/questions/__init__.py
CHANGED
@@ -120,10 +120,10 @@ from .question_budget import QuestionBudget
|
|
120
120
|
from .question_rank import QuestionRank
|
121
121
|
|
122
122
|
# Questions derived from core questions
|
123
|
-
from .
|
124
|
-
from .
|
125
|
-
from .
|
126
|
-
from .
|
123
|
+
from .question_likert_five import QuestionLikertFive
|
124
|
+
from .question_linear_scale import QuestionLinearScale
|
125
|
+
from .question_yes_no import QuestionYesNo
|
126
|
+
from .question_top_k import QuestionTopK
|
127
127
|
|
128
128
|
from .exceptions import QuestionScenarioRenderError
|
129
129
|
|
@@ -3,6 +3,7 @@
|
|
3
3
|
from .question_functional import QuestionFunctional
|
4
4
|
from .question_base import QuestionBase
|
5
5
|
from ..scenarios import Scenario
|
6
|
+
from .exceptions import QuestionValueError
|
6
7
|
|
7
8
|
|
8
9
|
def compose_questions(
|
@@ -19,7 +20,6 @@ def compose_questions(
|
|
19
20
|
if question_name is None:
|
20
21
|
question_name = f"{q1.question_name}_{q2.question_name}"
|
21
22
|
if q1.question_name not in q2.question_text:
|
22
|
-
from .exceptions import QuestionValueError
|
23
23
|
raise QuestionValueError(
|
24
24
|
f"q2 requires a field not present in q1's answer. "
|
25
25
|
f"q1: {q1.question_name}, q2: {q2.question_name}"
|
@@ -30,7 +30,7 @@ def compose_questions(
|
|
30
30
|
) -> QuestionFunctional:
|
31
31
|
"""Return the answer to the second question given the answer to the first question."""
|
32
32
|
# get the answer to the first question
|
33
|
-
from
|
33
|
+
from ..agents.agent import Agent
|
34
34
|
|
35
35
|
first_answer = (
|
36
36
|
q1.by(scenario)
|
edsl/questions/descriptors.py
CHANGED
@@ -230,7 +230,7 @@ class QuestionNameDescriptor(BaseDescriptor):
|
|
230
230
|
|
231
231
|
def validate(self, value, instance):
|
232
232
|
"""Validate the value is a valid variable name."""
|
233
|
-
from
|
233
|
+
from ..utilities.utilities import is_valid_variable_name
|
234
234
|
|
235
235
|
if "{{" in value and "}}" in value:
|
236
236
|
# they're trying to use a dynamic question name - let's let this play out
|
edsl/questions/question_base.py
CHANGED
@@ -271,8 +271,25 @@ class QuestionBase(
|
|
271
271
|
"""
|
272
272
|
if not hasattr(self, "_fake_data_factory"):
|
273
273
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
274
|
-
|
275
|
-
|
274
|
+
from random import randint, uniform
|
275
|
+
|
276
|
+
class FakeData(ModelFactory[self.response_model]):
|
277
|
+
# Add customization for specific question types
|
278
|
+
if hasattr(self, 'question_type') and self.question_type == 'numerical':
|
279
|
+
@classmethod
|
280
|
+
def build_answer(cls):
|
281
|
+
min_val = getattr(self, 'min_value', None)
|
282
|
+
max_val = getattr(self, 'max_value', None)
|
283
|
+
|
284
|
+
# Default values if none provided
|
285
|
+
min_val = 0 if min_val is None else min_val
|
286
|
+
max_val = 100 if max_val is None else max_val
|
287
|
+
|
288
|
+
# Ensure values are within bounds
|
289
|
+
if isinstance(min_val, int) and isinstance(max_val, int):
|
290
|
+
return randint(min_val, max_val)
|
291
|
+
else:
|
292
|
+
return uniform(min_val, max_val)
|
276
293
|
|
277
294
|
self._fake_data_factory = FakeData
|
278
295
|
return self._fake_data_factory
|
@@ -358,7 +375,19 @@ class QuestionBase(
|
|
358
375
|
>>> Q.example()._validate_answer({'answer': 'Hello', 'generated_tokens': 'Hello'})
|
359
376
|
{'answer': 'Hello', 'generated_tokens': 'Hello'}
|
360
377
|
"""
|
361
|
-
|
378
|
+
try:
|
379
|
+
return self.response_validator.validate(answer, replacement_dict)
|
380
|
+
except Exception as e:
|
381
|
+
# Ensure all validation errors are raised as QuestionAnswerValidationError
|
382
|
+
from .exceptions import QuestionAnswerValidationError
|
383
|
+
if not isinstance(e, QuestionAnswerValidationError):
|
384
|
+
raise QuestionAnswerValidationError(
|
385
|
+
message=f"Invalid response: {e}",
|
386
|
+
data=answer,
|
387
|
+
model=getattr(self, 'response_model', None),
|
388
|
+
pydantic_error=e if hasattr(e, 'errors') else None
|
389
|
+
) from e
|
390
|
+
raise
|
362
391
|
|
363
392
|
@property
|
364
393
|
def name(self) -> str:
|
@@ -74,7 +74,7 @@ class QuestionBasePromptsMixin:
|
|
74
74
|
>>> q.get_instructions(model = "gpt3")
|
75
75
|
Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
|
76
76
|
"""
|
77
|
-
from
|
77
|
+
from ..language_models.model import Model
|
78
78
|
|
79
79
|
if not hasattr(self, "_model_instructions"):
|
80
80
|
self._model_instructions = {}
|
@@ -125,7 +125,7 @@ class QuestionBasePromptsMixin:
|
|
125
125
|
template_text = template_manager.get_template(
|
126
126
|
cls.question_type, "answering_instructions.jinja"
|
127
127
|
)
|
128
|
-
from
|
128
|
+
from ..prompts import Prompt
|
129
129
|
|
130
130
|
return Prompt(text=template_text)
|
131
131
|
|
@@ -134,7 +134,7 @@ class QuestionBasePromptsMixin:
|
|
134
134
|
template_text = template_manager.get_template(
|
135
135
|
cls.question_type, "question_presentation.jinja"
|
136
136
|
)
|
137
|
-
from
|
137
|
+
from ..prompts import Prompt
|
138
138
|
|
139
139
|
return Prompt(text=template_text)
|
140
140
|
|
@@ -190,7 +190,7 @@ class QuestionBasePromptsMixin:
|
|
190
190
|
@property
|
191
191
|
def new_default_instructions(self) -> "Prompt":
|
192
192
|
"This is set up as a property because there are mutable question values that determine how it is rendered."
|
193
|
-
from
|
193
|
+
from ..prompts import Prompt
|
194
194
|
|
195
195
|
return Prompt(self.question_presentation) + Prompt(self.answering_instructions)
|
196
196
|
|