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.
Files changed (119) hide show
  1. edsl/__init__.py +45 -34
  2. edsl/__version__.py +1 -1
  3. edsl/base/base_exception.py +2 -2
  4. edsl/buckets/bucket_collection.py +1 -1
  5. edsl/buckets/exceptions.py +32 -0
  6. edsl/buckets/token_bucket_api.py +26 -10
  7. edsl/caching/cache.py +5 -2
  8. edsl/caching/remote_cache_sync.py +5 -5
  9. edsl/caching/sql_dict.py +12 -11
  10. edsl/config/__init__.py +1 -1
  11. edsl/config/config_class.py +4 -2
  12. edsl/conversation/Conversation.py +9 -5
  13. edsl/conversation/car_buying.py +1 -3
  14. edsl/conversation/mug_negotiation.py +2 -6
  15. edsl/coop/__init__.py +11 -8
  16. edsl/coop/coop.py +15 -13
  17. edsl/coop/coop_functions.py +1 -1
  18. edsl/coop/ep_key_handling.py +1 -1
  19. edsl/coop/price_fetcher.py +2 -2
  20. edsl/coop/utils.py +2 -2
  21. edsl/dataset/dataset.py +144 -63
  22. edsl/dataset/dataset_operations_mixin.py +14 -6
  23. edsl/dataset/dataset_tree.py +3 -3
  24. edsl/dataset/display/table_renderers.py +6 -3
  25. edsl/dataset/file_exports.py +4 -4
  26. edsl/dataset/r/ggplot.py +3 -3
  27. edsl/inference_services/available_model_fetcher.py +2 -2
  28. edsl/inference_services/data_structures.py +5 -5
  29. edsl/inference_services/inference_service_abc.py +1 -1
  30. edsl/inference_services/inference_services_collection.py +1 -1
  31. edsl/inference_services/service_availability.py +3 -3
  32. edsl/inference_services/services/azure_ai.py +3 -3
  33. edsl/inference_services/services/google_service.py +1 -1
  34. edsl/inference_services/services/test_service.py +1 -1
  35. edsl/instructions/change_instruction.py +5 -4
  36. edsl/instructions/instruction.py +1 -0
  37. edsl/instructions/instruction_collection.py +5 -4
  38. edsl/instructions/instruction_handler.py +10 -8
  39. edsl/interviews/answering_function.py +20 -21
  40. edsl/interviews/exception_tracking.py +3 -2
  41. edsl/interviews/interview.py +1 -1
  42. edsl/interviews/interview_status_dictionary.py +1 -1
  43. edsl/interviews/interview_task_manager.py +7 -4
  44. edsl/interviews/request_token_estimator.py +3 -2
  45. edsl/interviews/statistics.py +2 -2
  46. edsl/invigilators/invigilators.py +34 -6
  47. edsl/jobs/__init__.py +39 -2
  48. edsl/jobs/async_interview_runner.py +1 -1
  49. edsl/jobs/check_survey_scenario_compatibility.py +5 -5
  50. edsl/jobs/data_structures.py +2 -2
  51. edsl/jobs/html_table_job_logger.py +494 -257
  52. edsl/jobs/jobs.py +2 -2
  53. edsl/jobs/jobs_checks.py +5 -5
  54. edsl/jobs/jobs_component_constructor.py +2 -2
  55. edsl/jobs/jobs_pricing_estimation.py +1 -1
  56. edsl/jobs/jobs_runner_asyncio.py +2 -2
  57. edsl/jobs/jobs_status_enums.py +1 -0
  58. edsl/jobs/remote_inference.py +47 -13
  59. edsl/jobs/results_exceptions_handler.py +2 -2
  60. edsl/language_models/language_model.py +151 -145
  61. edsl/notebooks/__init__.py +24 -1
  62. edsl/notebooks/exceptions.py +82 -0
  63. edsl/notebooks/notebook.py +7 -3
  64. edsl/notebooks/notebook_to_latex.py +1 -1
  65. edsl/prompts/__init__.py +23 -2
  66. edsl/prompts/prompt.py +1 -1
  67. edsl/questions/__init__.py +4 -4
  68. edsl/questions/answer_validator_mixin.py +0 -5
  69. edsl/questions/compose_questions.py +2 -2
  70. edsl/questions/descriptors.py +1 -1
  71. edsl/questions/question_base.py +32 -3
  72. edsl/questions/question_base_prompts_mixin.py +4 -4
  73. edsl/questions/question_budget.py +503 -102
  74. edsl/questions/question_check_box.py +658 -156
  75. edsl/questions/question_dict.py +176 -2
  76. edsl/questions/question_extract.py +401 -61
  77. edsl/questions/question_free_text.py +77 -9
  78. edsl/questions/question_functional.py +118 -9
  79. edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
  80. edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
  81. edsl/questions/question_list.py +246 -26
  82. edsl/questions/question_matrix.py +586 -73
  83. edsl/questions/question_multiple_choice.py +213 -47
  84. edsl/questions/question_numerical.py +360 -29
  85. edsl/questions/question_rank.py +401 -124
  86. edsl/questions/question_registry.py +3 -3
  87. edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
  88. edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
  89. edsl/questions/register_questions_meta.py +2 -1
  90. edsl/questions/response_validator_abc.py +6 -2
  91. edsl/questions/response_validator_factory.py +10 -12
  92. edsl/results/report.py +1 -1
  93. edsl/results/result.py +7 -4
  94. edsl/results/results.py +500 -271
  95. edsl/results/results_selector.py +2 -2
  96. edsl/scenarios/construct_download_link.py +3 -3
  97. edsl/scenarios/scenario.py +1 -2
  98. edsl/scenarios/scenario_list.py +41 -23
  99. edsl/surveys/survey_css.py +3 -3
  100. edsl/surveys/survey_simulator.py +2 -1
  101. edsl/tasks/__init__.py +22 -2
  102. edsl/tasks/exceptions.py +72 -0
  103. edsl/tasks/task_history.py +48 -11
  104. edsl/templates/error_reporting/base.html +37 -4
  105. edsl/templates/error_reporting/exceptions_table.html +105 -33
  106. edsl/templates/error_reporting/interview_details.html +130 -126
  107. edsl/templates/error_reporting/overview.html +21 -25
  108. edsl/templates/error_reporting/report.css +215 -46
  109. edsl/templates/error_reporting/report.js +122 -20
  110. edsl/tokens/__init__.py +27 -1
  111. edsl/tokens/exceptions.py +37 -0
  112. edsl/tokens/interview_token_usage.py +3 -2
  113. edsl/tokens/token_usage.py +4 -3
  114. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/METADATA +1 -1
  115. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/RECORD +118 -116
  116. edsl/questions/derived/__init__.py +0 -0
  117. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/LICENSE +0 -0
  118. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/WHEEL +0 -0
  119. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/entry_points.txt +0 -0
edsl/jobs/jobs.py CHANGED
@@ -620,7 +620,7 @@ class Jobs(Base):
620
620
  if jh.use_remote_inference(self.run_config.parameters.disable_remote_inference):
621
621
  job_info: RemoteJobInfo = self._start_remote_inference_job(jh)
622
622
  if background:
623
- from edsl.results import Results
623
+ from ..results import Results
624
624
 
625
625
  results = Results.from_job_info(job_info)
626
626
  return results, None
@@ -881,7 +881,7 @@ class Jobs(Base):
881
881
  ],
882
882
  }
883
883
  if add_edsl_version:
884
- from edsl import __version__
884
+ from .. import __version__
885
885
 
886
886
  d["edsl_version"] = __version__
887
887
  d["edsl_class_name"] = "Jobs"
edsl/jobs/jobs_checks.py CHANGED
@@ -3,7 +3,7 @@ Checks a Jobs object for missing API keys and other requirements.
3
3
  """
4
4
 
5
5
  import os
6
- from edsl.key_management.key_lookup_builder import MissingAPIKeyError
6
+ from ..key_management.key_lookup_builder import MissingAPIKeyError
7
7
 
8
8
 
9
9
  class JobsChecks:
@@ -16,7 +16,7 @@ class JobsChecks:
16
16
  self.jobs = jobs
17
17
 
18
18
  def check_api_keys(self) -> None:
19
- from edsl.language_models.model import Model
19
+ from ..language_models.model import Model
20
20
 
21
21
  if len(self.jobs.models) == 0:
22
22
  models = [Model()]
@@ -37,7 +37,7 @@ class JobsChecks:
37
37
  """
38
38
  missing_api_keys = set()
39
39
 
40
- from edsl.enums import service_to_api_keyname
40
+ from ..enums import service_to_api_keyname
41
41
 
42
42
  for model in self.jobs.models: # + [Model()]:
43
43
  if not model.has_valid_api_key():
@@ -134,8 +134,8 @@ class JobsChecks:
134
134
  def key_process(self):
135
135
  import secrets
136
136
  from dotenv import load_dotenv
137
- from edsl.coop.coop import Coop
138
- from edsl.utilities.utilities import write_api_key_to_env
137
+ from ..coop.coop import Coop
138
+ from ..utilities.utilities import write_api_key_to_env
139
139
 
140
140
  missing_api_keys = self.get_missing_api_keys()
141
141
 
@@ -140,8 +140,8 @@ class JobsComponentConstructor:
140
140
 
141
141
  @staticmethod
142
142
  def _get_empty_container_object(object):
143
- from edsl.agents import AgentList
144
- from edsl.scenarios import ScenarioList
143
+ from ..agents import AgentList
144
+ from ..scenarios import ScenarioList
145
145
 
146
146
  return {"Agent": AgentList([]), "Scenario": ScenarioList([])}.get(
147
147
  object.__class__.__name__, []
@@ -137,7 +137,7 @@ class JobsPrompts:
137
137
  def price_lookup(self) -> dict:
138
138
  """Fetches the price lookup from Coop if it is not already cached."""
139
139
  if self._price_lookup is None:
140
- from edsl.coop.coop import Coop
140
+ from ..coop.coop import Coop
141
141
 
142
142
  c = Coop()
143
143
  self._price_lookup = c.fetch_prices()
@@ -186,7 +186,7 @@ class JobsRunnerAsyncio:
186
186
  self.start_time = time.monotonic()
187
187
  self.completed = False
188
188
 
189
- from edsl.coop import Coop
189
+ from ..coop import Coop
190
190
 
191
191
  coop = Coop()
192
192
  endpoint_url = coop.get_progress_bar_url()
@@ -273,7 +273,7 @@ class JobsRunnerAsyncio:
273
273
  # breakpoint()
274
274
  results.bucket_collection = self.environment.bucket_collection
275
275
 
276
- from edsl.jobs.results_exceptions_handler import ResultsExceptionsHandler
276
+ from .results_exceptions_handler import ResultsExceptionsHandler
277
277
 
278
278
  results_exceptions_handler = ResultsExceptionsHandler(results, parameters)
279
279
 
@@ -5,5 +5,6 @@ class JobsStatus(Enum):
5
5
  QUEUED = "queued"
6
6
  RUNNING = "running"
7
7
  COMPLETED = "completed"
8
+ PARTIALLY_FAILED = "partially_failed"
8
9
  FAILED = "failed"
9
10
  CANCELLED = "cancelled"
@@ -43,7 +43,7 @@ class JobsRemoteInferenceHandler:
43
43
  self.verbose = verbose
44
44
  self.poll_interval = poll_interval
45
45
 
46
- from edsl.config import CONFIG
46
+ from ..config import CONFIG
47
47
 
48
48
  self.expected_parrot_url = CONFIG.get("EXPECTED_PARROT_URL")
49
49
  self.remote_inference_url = f"{self.expected_parrot_url}/home/remote-inference"
@@ -113,7 +113,7 @@ class JobsRemoteInferenceHandler:
113
113
  logger.add_info("job_uuid", job_uuid)
114
114
 
115
115
  logger.update(
116
- f"Job details are available at your Coop account {self.remote_inference_url}",
116
+ f"Job details are available at your Coop account. [Go to Remote Inference page]({self.remote_inference_url})",
117
117
  status=JobsStatus.RUNNING,
118
118
  )
119
119
  progress_bar_url = (
@@ -121,7 +121,7 @@ class JobsRemoteInferenceHandler:
121
121
  )
122
122
  logger.add_info("progress_bar_url", progress_bar_url)
123
123
  logger.update(
124
- f"View job progress here: {progress_bar_url}", status=JobsStatus.RUNNING
124
+ f"View job progress [here]({progress_bar_url})", status=JobsStatus.RUNNING
125
125
  )
126
126
 
127
127
  return RemoteJobInfo(
@@ -169,7 +169,7 @@ class JobsRemoteInferenceHandler:
169
169
  message="Job cancelled by the user.", status=JobsStatus.CANCELLED
170
170
  )
171
171
  job_info.logger.update(
172
- f"See {self.expected_parrot_url}/home/remote-inference for more details.",
172
+ f"See [Remote Inference page]({self.expected_parrot_url}/home/remote-inference) for more details.",
173
173
  status=JobsStatus.CANCELLED,
174
174
  )
175
175
 
@@ -182,22 +182,45 @@ class JobsRemoteInferenceHandler:
182
182
  reason = remote_job_data.get("reason")
183
183
 
184
184
  if reason == "insufficient funds":
185
- latest_error_report_url = "Error: Insufficient balance to start the job"
186
- print("Error: Insufficient balance to start the job")
185
+ job_info.logger.update(
186
+ f"Error: Insufficient balance to start the job. Add funds to your account at the [Credits page]({self.expected_parrot_url}/home/credits)",
187
+ status=JobsStatus.FAILED,
188
+ )
187
189
 
188
190
  if latest_error_report_url:
189
191
  job_info.logger.add_info("error_report_url", latest_error_report_url)
190
192
 
191
193
  job_info.logger.update("Job failed.", status=JobsStatus.FAILED)
192
194
  job_info.logger.update(
193
- f"See {self.expected_parrot_url}/home/remote-inference for more details.",
195
+ f"See [Remote Inference page]({self.expected_parrot_url}/home/remote-inference) for more details.",
194
196
  status=JobsStatus.FAILED,
195
197
  )
196
198
  job_info.logger.update(
197
- f"Need support? Visit Discord: {RemoteJobConstants.DISCORD_URL}",
199
+ f"Need support? [Visit Discord]({RemoteJobConstants.DISCORD_URL})",
198
200
  status=JobsStatus.FAILED,
199
201
  )
200
202
 
203
+ def _handle_partially_failed_job(
204
+ self, job_info: RemoteJobInfo, remote_job_data: RemoteInferenceResponse
205
+ ) -> None:
206
+ "Handles a partially failed job by logging the error and updating the job status."
207
+ latest_error_report_url = remote_job_data.get("latest_error_report_url")
208
+
209
+ if latest_error_report_url:
210
+ job_info.logger.add_info("error_report_url", latest_error_report_url)
211
+
212
+ job_info.logger.update(
213
+ "Job completed with partial results.", status=JobsStatus.PARTIALLY_FAILED
214
+ )
215
+ job_info.logger.update(
216
+ f"See [Remote Inference page]({self.expected_parrot_url}/home/remote-inference) for more details.",
217
+ status=JobsStatus.PARTIALLY_FAILED,
218
+ )
219
+ job_info.logger.update(
220
+ f"Need support? [Visit Discord]({RemoteJobConstants.DISCORD_URL})",
221
+ status=JobsStatus.PARTIALLY_FAILED,
222
+ )
223
+
201
224
  def _sleep_for_a_bit(self, job_info: RemoteJobInfo, status: str) -> None:
202
225
  import time
203
226
  from datetime import datetime
@@ -212,6 +235,7 @@ class JobsRemoteInferenceHandler:
212
235
  def _fetch_results_and_log(
213
236
  self,
214
237
  job_info: RemoteJobInfo,
238
+ job_status: Literal["failed", "partial_failed", "completed"],
215
239
  results_uuid: str,
216
240
  remote_job_data: RemoteInferenceResponse,
217
241
  object_fetcher: Callable,
@@ -221,10 +245,17 @@ class JobsRemoteInferenceHandler:
221
245
  results = object_fetcher(results_uuid, expected_object_type="results")
222
246
  results_url = remote_job_data.get("results_url")
223
247
  job_info.logger.add_info("results_url", results_url)
224
- job_info.logger.update(
225
- f"Job completed and Results stored on Coop: {results_url}",
226
- status=JobsStatus.COMPLETED,
227
- )
248
+
249
+ if job_status == "completed":
250
+ job_info.logger.update(
251
+ f"Job completed and Results stored on Coop. [View Results]({results_url})",
252
+ status=JobsStatus.COMPLETED,
253
+ )
254
+ elif job_status == "partial_failed":
255
+ job_info.logger.update(
256
+ f"View partial results [here]({results_url})",
257
+ status=JobsStatus.PARTIALLY_FAILED,
258
+ )
228
259
  results.job_uuid = job_info.job_uuid
229
260
  results.results_uuid = results_uuid
230
261
  return results
@@ -244,13 +275,16 @@ class JobsRemoteInferenceHandler:
244
275
  return None, reason
245
276
 
246
277
  elif status == "failed" or status == "completed" or status == "partial_failed":
247
- if status == "failed" or status == "partial_failed":
278
+ if status == "failed":
248
279
  self._handle_failed_job(job_info, remote_job_data)
280
+ elif status == "partial_failed":
281
+ self._handle_partially_failed_job(job_info, remote_job_data)
249
282
 
250
283
  results_uuid = remote_job_data.get("results_uuid")
251
284
  if results_uuid:
252
285
  results = self._fetch_results_and_log(
253
286
  job_info=job_info,
287
+ job_status=status,
254
288
  results_uuid=results_uuid,
255
289
  remote_job_data=remote_job_data,
256
290
  object_fetcher=object_fetcher,
@@ -1,8 +1,8 @@
1
1
  from typing import Protocol
2
2
  import sys
3
3
  #from edsl.scenarios.FileStore import HTMLFileStore
4
- from edsl.config import CONFIG
5
- from edsl.coop.coop import Coop
4
+ from ..config import CONFIG
5
+ from ..coop.coop import Coop
6
6
  from ..scenarios import FileStore
7
7
  from .exceptions import JobsErrors
8
8