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.
Files changed (109) hide show
  1. edsl/__version__.py +1 -1
  2. edsl/base/base_exception.py +2 -2
  3. edsl/buckets/bucket_collection.py +1 -1
  4. edsl/buckets/exceptions.py +32 -0
  5. edsl/buckets/token_bucket_api.py +26 -10
  6. edsl/caching/cache.py +5 -2
  7. edsl/caching/remote_cache_sync.py +5 -5
  8. edsl/caching/sql_dict.py +12 -11
  9. edsl/config/__init__.py +1 -1
  10. edsl/config/config_class.py +4 -2
  11. edsl/conversation/Conversation.py +7 -4
  12. edsl/conversation/car_buying.py +1 -3
  13. edsl/conversation/mug_negotiation.py +2 -6
  14. edsl/coop/__init__.py +11 -8
  15. edsl/coop/coop.py +13 -13
  16. edsl/coop/coop_functions.py +1 -1
  17. edsl/coop/ep_key_handling.py +1 -1
  18. edsl/coop/price_fetcher.py +2 -2
  19. edsl/coop/utils.py +2 -2
  20. edsl/dataset/dataset.py +144 -63
  21. edsl/dataset/dataset_operations_mixin.py +14 -6
  22. edsl/dataset/dataset_tree.py +3 -3
  23. edsl/dataset/display/table_renderers.py +6 -3
  24. edsl/dataset/file_exports.py +4 -4
  25. edsl/dataset/r/ggplot.py +3 -3
  26. edsl/inference_services/available_model_fetcher.py +2 -2
  27. edsl/inference_services/data_structures.py +5 -5
  28. edsl/inference_services/inference_service_abc.py +1 -1
  29. edsl/inference_services/inference_services_collection.py +1 -1
  30. edsl/inference_services/service_availability.py +3 -3
  31. edsl/inference_services/services/azure_ai.py +3 -3
  32. edsl/inference_services/services/google_service.py +1 -1
  33. edsl/inference_services/services/test_service.py +1 -1
  34. edsl/instructions/change_instruction.py +5 -4
  35. edsl/instructions/instruction.py +1 -0
  36. edsl/instructions/instruction_collection.py +5 -4
  37. edsl/instructions/instruction_handler.py +10 -8
  38. edsl/interviews/exception_tracking.py +1 -1
  39. edsl/interviews/interview.py +1 -1
  40. edsl/interviews/interview_status_dictionary.py +1 -1
  41. edsl/interviews/interview_task_manager.py +2 -2
  42. edsl/interviews/request_token_estimator.py +3 -2
  43. edsl/interviews/statistics.py +2 -2
  44. edsl/invigilators/invigilators.py +2 -2
  45. edsl/jobs/__init__.py +39 -2
  46. edsl/jobs/async_interview_runner.py +1 -1
  47. edsl/jobs/check_survey_scenario_compatibility.py +5 -5
  48. edsl/jobs/data_structures.py +2 -2
  49. edsl/jobs/jobs.py +2 -2
  50. edsl/jobs/jobs_checks.py +5 -5
  51. edsl/jobs/jobs_component_constructor.py +2 -2
  52. edsl/jobs/jobs_pricing_estimation.py +1 -1
  53. edsl/jobs/jobs_runner_asyncio.py +2 -2
  54. edsl/jobs/remote_inference.py +1 -1
  55. edsl/jobs/results_exceptions_handler.py +2 -2
  56. edsl/language_models/language_model.py +5 -1
  57. edsl/notebooks/__init__.py +24 -1
  58. edsl/notebooks/exceptions.py +82 -0
  59. edsl/notebooks/notebook.py +7 -3
  60. edsl/notebooks/notebook_to_latex.py +1 -1
  61. edsl/prompts/__init__.py +23 -2
  62. edsl/prompts/prompt.py +1 -1
  63. edsl/questions/__init__.py +4 -4
  64. edsl/questions/answer_validator_mixin.py +0 -5
  65. edsl/questions/compose_questions.py +2 -2
  66. edsl/questions/descriptors.py +1 -1
  67. edsl/questions/question_base.py +32 -3
  68. edsl/questions/question_base_prompts_mixin.py +4 -4
  69. edsl/questions/question_budget.py +503 -102
  70. edsl/questions/question_check_box.py +658 -156
  71. edsl/questions/question_dict.py +176 -2
  72. edsl/questions/question_extract.py +401 -61
  73. edsl/questions/question_free_text.py +77 -9
  74. edsl/questions/question_functional.py +118 -9
  75. edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
  76. edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
  77. edsl/questions/question_list.py +246 -26
  78. edsl/questions/question_matrix.py +586 -73
  79. edsl/questions/question_multiple_choice.py +213 -47
  80. edsl/questions/question_numerical.py +360 -29
  81. edsl/questions/question_rank.py +401 -124
  82. edsl/questions/question_registry.py +3 -3
  83. edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
  84. edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
  85. edsl/questions/register_questions_meta.py +2 -1
  86. edsl/questions/response_validator_abc.py +6 -2
  87. edsl/questions/response_validator_factory.py +10 -12
  88. edsl/results/report.py +1 -1
  89. edsl/results/result.py +7 -4
  90. edsl/results/results.py +471 -271
  91. edsl/results/results_selector.py +2 -2
  92. edsl/scenarios/construct_download_link.py +3 -3
  93. edsl/scenarios/scenario.py +1 -2
  94. edsl/scenarios/scenario_list.py +41 -23
  95. edsl/surveys/survey_css.py +3 -3
  96. edsl/surveys/survey_simulator.py +2 -1
  97. edsl/tasks/__init__.py +22 -2
  98. edsl/tasks/exceptions.py +72 -0
  99. edsl/tasks/task_history.py +3 -3
  100. edsl/tokens/__init__.py +27 -1
  101. edsl/tokens/exceptions.py +37 -0
  102. edsl/tokens/interview_token_usage.py +3 -2
  103. edsl/tokens/token_usage.py +4 -3
  104. {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/METADATA +1 -1
  105. {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/RECORD +108 -106
  106. edsl/questions/derived/__init__.py +0 -0
  107. {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/LICENSE +0 -0
  108. {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/WHEEL +0 -0
  109. {edsl-0.1.50.dist-info → edsl-0.1.51.dist-info}/entry_points.txt +0 -0
@@ -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 .derived.question_likert_five import QuestionLikertFive
124
- from .derived.question_linear_scale import QuestionLinearScale
125
- from .derived.question_yes_no import QuestionYesNo
126
- from .derived.question_top_k import QuestionTopK
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
 
@@ -1,10 +1,5 @@
1
1
  """Mixin with validators for LLM answers to questions."""
2
2
 
3
- import re
4
- from typing import Any, Type, Union
5
- from .exceptions import (
6
- QuestionAnswerValidationError,
7
- )
8
3
 
9
4
 
10
5
  class AnswerValidatorMixin:
@@ -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 edsl.agents.Agent import Agent
33
+ from ..agents.agent import Agent
34
34
 
35
35
  first_answer = (
36
36
  q1.by(scenario)
@@ -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 edsl.utilities.utilities import is_valid_variable_name
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
@@ -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
- class FakeData(ModelFactory[self.response_model]): ...
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
- return self.response_validator.validate(answer, replacement_dict)
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 edsl.language_models.model import Model
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 edsl.prompts import Prompt
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 edsl.prompts import Prompt
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 edsl.prompts import Prompt
193
+ from ..prompts import Prompt
194
194
 
195
195
  return Prompt(self.question_presentation) + Prompt(self.answering_instructions)
196
196