edsl 0.1.31.dev4__py3-none-any.whl → 0.1.33__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/Base.py +9 -3
- edsl/TemplateLoader.py +24 -0
- edsl/__init__.py +8 -3
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +40 -8
- edsl/agents/AgentList.py +43 -0
- edsl/agents/Invigilator.py +136 -221
- edsl/agents/InvigilatorBase.py +148 -59
- edsl/agents/{PromptConstructionMixin.py → PromptConstructor.py} +154 -85
- edsl/agents/__init__.py +1 -0
- edsl/auto/AutoStudy.py +117 -0
- edsl/auto/StageBase.py +230 -0
- edsl/auto/StageGenerateSurvey.py +178 -0
- edsl/auto/StageLabelQuestions.py +125 -0
- edsl/auto/StagePersona.py +61 -0
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
- edsl/auto/StagePersonaDimensionValues.py +74 -0
- edsl/auto/StagePersonaDimensions.py +69 -0
- edsl/auto/StageQuestions.py +73 -0
- edsl/auto/SurveyCreatorPipeline.py +21 -0
- edsl/auto/utilities.py +224 -0
- edsl/config.py +48 -47
- edsl/conjure/Conjure.py +6 -0
- edsl/coop/PriceFetcher.py +58 -0
- edsl/coop/coop.py +50 -7
- edsl/data/Cache.py +35 -1
- edsl/data/CacheHandler.py +3 -4
- edsl/data_transfer_models.py +73 -38
- edsl/enums.py +8 -0
- edsl/exceptions/general.py +10 -8
- edsl/exceptions/language_models.py +25 -1
- edsl/exceptions/questions.py +62 -5
- edsl/exceptions/results.py +4 -0
- edsl/inference_services/AnthropicService.py +13 -11
- edsl/inference_services/AwsBedrock.py +112 -0
- edsl/inference_services/AzureAI.py +214 -0
- edsl/inference_services/DeepInfraService.py +4 -3
- edsl/inference_services/GoogleService.py +16 -12
- edsl/inference_services/GroqService.py +5 -4
- edsl/inference_services/InferenceServiceABC.py +58 -3
- edsl/inference_services/InferenceServicesCollection.py +13 -8
- edsl/inference_services/MistralAIService.py +120 -0
- edsl/inference_services/OllamaService.py +18 -0
- edsl/inference_services/OpenAIService.py +55 -56
- edsl/inference_services/TestService.py +80 -0
- edsl/inference_services/TogetherAIService.py +170 -0
- edsl/inference_services/models_available_cache.py +25 -0
- edsl/inference_services/registry.py +19 -1
- edsl/jobs/Answers.py +10 -12
- edsl/jobs/FailedQuestion.py +78 -0
- edsl/jobs/Jobs.py +137 -41
- edsl/jobs/buckets/BucketCollection.py +24 -15
- edsl/jobs/buckets/TokenBucket.py +105 -18
- edsl/jobs/interviews/Interview.py +393 -83
- edsl/jobs/interviews/{interview_exception_tracking.py → InterviewExceptionCollection.py} +22 -18
- edsl/jobs/interviews/InterviewExceptionEntry.py +167 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +152 -160
- edsl/jobs/runners/JobsRunnerStatus.py +331 -0
- edsl/jobs/tasks/QuestionTaskCreator.py +30 -23
- edsl/jobs/tasks/TaskCreators.py +1 -1
- edsl/jobs/tasks/TaskHistory.py +205 -126
- edsl/language_models/LanguageModel.py +297 -177
- edsl/language_models/ModelList.py +2 -2
- edsl/language_models/RegisterLanguageModelsMeta.py +14 -29
- edsl/language_models/fake_openai_call.py +15 -0
- edsl/language_models/fake_openai_service.py +61 -0
- edsl/language_models/registry.py +25 -8
- edsl/language_models/repair.py +0 -19
- edsl/language_models/utilities.py +61 -0
- edsl/notebooks/Notebook.py +20 -2
- edsl/prompts/Prompt.py +52 -2
- edsl/questions/AnswerValidatorMixin.py +23 -26
- edsl/questions/QuestionBase.py +330 -249
- edsl/questions/QuestionBaseGenMixin.py +133 -0
- edsl/questions/QuestionBasePromptsMixin.py +266 -0
- edsl/questions/QuestionBudget.py +99 -42
- edsl/questions/QuestionCheckBox.py +227 -36
- edsl/questions/QuestionExtract.py +98 -28
- edsl/questions/QuestionFreeText.py +47 -31
- edsl/questions/QuestionFunctional.py +7 -0
- edsl/questions/QuestionList.py +141 -23
- edsl/questions/QuestionMultipleChoice.py +159 -66
- edsl/questions/QuestionNumerical.py +88 -47
- edsl/questions/QuestionRank.py +182 -25
- edsl/questions/Quick.py +41 -0
- edsl/questions/RegisterQuestionsMeta.py +31 -12
- edsl/questions/ResponseValidatorABC.py +170 -0
- edsl/questions/__init__.py +3 -4
- edsl/questions/decorators.py +21 -0
- edsl/questions/derived/QuestionLikertFive.py +10 -5
- edsl/questions/derived/QuestionLinearScale.py +15 -2
- edsl/questions/derived/QuestionTopK.py +10 -1
- edsl/questions/derived/QuestionYesNo.py +24 -3
- edsl/questions/descriptors.py +43 -7
- edsl/questions/prompt_templates/question_budget.jinja +13 -0
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
- edsl/questions/prompt_templates/question_extract.jinja +11 -0
- edsl/questions/prompt_templates/question_free_text.jinja +3 -0
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
- edsl/questions/prompt_templates/question_list.jinja +17 -0
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
- edsl/questions/prompt_templates/question_numerical.jinja +37 -0
- edsl/questions/question_registry.py +6 -2
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +7 -0
- edsl/questions/templates/budget/question_presentation.jinja +7 -0
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/extract/answering_instructions.jinja +7 -0
- edsl/questions/templates/extract/question_presentation.jinja +1 -0
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +1 -0
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
- edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +4 -0
- edsl/questions/templates/list/question_presentation.jinja +5 -0
- edsl/questions/templates/multiple_choice/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +8 -0
- edsl/questions/templates/numerical/question_presentation.jinja +7 -0
- edsl/questions/templates/rank/__init__.py +0 -0
- edsl/questions/templates/rank/answering_instructions.jinja +11 -0
- edsl/questions/templates/rank/question_presentation.jinja +15 -0
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
- edsl/questions/templates/top_k/question_presentation.jinja +22 -0
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
- edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
- edsl/results/Dataset.py +20 -0
- edsl/results/DatasetExportMixin.py +58 -30
- edsl/results/DatasetTree.py +145 -0
- edsl/results/Result.py +32 -5
- edsl/results/Results.py +135 -46
- edsl/results/ResultsDBMixin.py +3 -3
- edsl/results/Selector.py +118 -0
- edsl/results/tree_explore.py +115 -0
- edsl/scenarios/FileStore.py +71 -10
- edsl/scenarios/Scenario.py +109 -24
- edsl/scenarios/ScenarioImageMixin.py +2 -2
- edsl/scenarios/ScenarioList.py +546 -21
- edsl/scenarios/ScenarioListExportMixin.py +24 -4
- edsl/scenarios/ScenarioListPdfMixin.py +153 -4
- edsl/study/SnapShot.py +8 -1
- edsl/study/Study.py +32 -0
- edsl/surveys/Rule.py +15 -3
- edsl/surveys/RuleCollection.py +21 -5
- edsl/surveys/Survey.py +707 -298
- edsl/surveys/SurveyExportMixin.py +71 -9
- edsl/surveys/SurveyFlowVisualizationMixin.py +2 -1
- edsl/surveys/SurveyQualtricsImport.py +284 -0
- edsl/surveys/instructions/ChangeInstruction.py +47 -0
- edsl/surveys/instructions/Instruction.py +34 -0
- edsl/surveys/instructions/InstructionCollection.py +77 -0
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +24 -0
- edsl/templates/error_reporting/exceptions_by_model.html +35 -0
- edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
- edsl/templates/error_reporting/exceptions_by_type.html +17 -0
- edsl/templates/error_reporting/interview_details.html +116 -0
- edsl/templates/error_reporting/interviews.html +10 -0
- edsl/templates/error_reporting/overview.html +5 -0
- edsl/templates/error_reporting/performance_plot.html +2 -0
- edsl/templates/error_reporting/report.css +74 -0
- edsl/templates/error_reporting/report.html +118 -0
- edsl/templates/error_reporting/report.js +25 -0
- edsl/utilities/utilities.py +40 -1
- {edsl-0.1.31.dev4.dist-info → edsl-0.1.33.dist-info}/METADATA +8 -2
- edsl-0.1.33.dist-info/RECORD +295 -0
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +0 -271
- edsl/jobs/interviews/retry_management.py +0 -37
- edsl/jobs/runners/JobsRunnerStatusMixin.py +0 -303
- edsl/utilities/gcp_bucket/simple_example.py +0 -9
- edsl-0.1.31.dev4.dist-info/RECORD +0 -204
- {edsl-0.1.31.dev4.dist-info → edsl-0.1.33.dist-info}/LICENSE +0 -0
- {edsl-0.1.31.dev4.dist-info → edsl-0.1.33.dist-info}/WHEEL +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
from edsl.questions import QuestionBase
|
2
|
+
from edsl import Question, Scenario, Model, Agent
|
3
|
+
|
4
|
+
from edsl.language_models.LanguageModel import LanguageModel
|
5
|
+
|
6
|
+
|
7
|
+
class FailedQuestion:
|
8
|
+
# tests/jobs/test_Interview.py::test_handle_model_exceptions
|
9
|
+
|
10
|
+
# (Pdb) dir(self.exception.__traceback__)
|
11
|
+
# ['tb_frame', 'tb_lasti', 'tb_lineno', 'tb_next']
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self, question, scenario, model, agent, raw_model_response, exception, prompts
|
15
|
+
):
|
16
|
+
self.question = question
|
17
|
+
self.scenario = scenario
|
18
|
+
self.model = model
|
19
|
+
self.agent = agent
|
20
|
+
self.raw_model_response = raw_model_response # JSON
|
21
|
+
self.exception = exception
|
22
|
+
self.prompts = prompts
|
23
|
+
|
24
|
+
def to_dict(self):
|
25
|
+
return {
|
26
|
+
"question": self.question._to_dict(),
|
27
|
+
"scenario": self.scenario._to_dict(),
|
28
|
+
"model": self.model._to_dict(),
|
29
|
+
"agent": self.agent._to_dict(),
|
30
|
+
"raw_model_response": self.raw_model_response,
|
31
|
+
"exception": self.exception.__class__.__name__, # self.exception,
|
32
|
+
"prompts": self.prompts,
|
33
|
+
}
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def from_dict(cls, data):
|
37
|
+
question = QuestionBase.from_dict(data["question"])
|
38
|
+
scenario = Scenario.from_dict(data["scenario"])
|
39
|
+
model = LanguageModel.from_dict(data["model"])
|
40
|
+
agent = Agent.from_dict(data["agent"])
|
41
|
+
raw_model_response = data["raw_model_response"]
|
42
|
+
exception = data["exception"]
|
43
|
+
prompts = data["prompts"]
|
44
|
+
return cls(
|
45
|
+
question, scenario, model, agent, raw_model_response, exception, prompts
|
46
|
+
)
|
47
|
+
|
48
|
+
def __repr__(self):
|
49
|
+
return f"{self.__class__.__name__}(question={repr(self.question)}, scenario={repr(self.scenario)}, model={repr(self.model)}, agent={repr(self.agent)}, raw_model_response={repr(self.raw_model_response)}, exception={repr(self.exception)})"
|
50
|
+
|
51
|
+
@property
|
52
|
+
def jobs(self):
|
53
|
+
return self.question.by(self.scenario).by(self.agent).by(self.model)
|
54
|
+
|
55
|
+
def rerun(self):
|
56
|
+
results = self.jobs.run()
|
57
|
+
return results
|
58
|
+
|
59
|
+
def help(self):
|
60
|
+
pass
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def example(cls):
|
64
|
+
from edsl.language_models.utilities import create_language_model
|
65
|
+
from edsl.language_models.utilities import create_survey
|
66
|
+
|
67
|
+
survey = create_survey(2, chained=False, take_scenario=False)
|
68
|
+
fail_at_number = 1
|
69
|
+
model = create_language_model(ValueError, fail_at_number)()
|
70
|
+
from edsl import Survey
|
71
|
+
|
72
|
+
results = survey.by(model).run()
|
73
|
+
return results.failed_questions[0][0]
|
74
|
+
|
75
|
+
|
76
|
+
if __name__ == "__main__":
|
77
|
+
fq = FailedQuestion.example()
|
78
|
+
new_fq = FailedQuestion.from_dict(fq.to_dict())
|
edsl/jobs/Jobs.py
CHANGED
@@ -39,6 +39,8 @@ class Jobs(Base):
|
|
39
39
|
|
40
40
|
self.__bucket_collection = None
|
41
41
|
|
42
|
+
# these setters and getters are used to ensure that the agents, models, and scenarios are stored as AgentList, ModelList, and ScenarioList objects
|
43
|
+
|
42
44
|
@property
|
43
45
|
def models(self):
|
44
46
|
return self._models
|
@@ -119,7 +121,9 @@ class Jobs(Base):
|
|
119
121
|
- scenarios: traits of new scenarios are combined with traits of old existing. New scenarios will overwrite overlapping traits, and do not increase the number of scenarios in the instance
|
120
122
|
- models: new models overwrite old models.
|
121
123
|
"""
|
122
|
-
passed_objects = self._turn_args_to_list(
|
124
|
+
passed_objects = self._turn_args_to_list(
|
125
|
+
args
|
126
|
+
) # objects can also be passed comma-separated
|
123
127
|
|
124
128
|
current_objects, objects_key = self._get_current_objects_of_this_type(
|
125
129
|
passed_objects[0]
|
@@ -152,7 +156,11 @@ class Jobs(Base):
|
|
152
156
|
from edsl.results.Dataset import Dataset
|
153
157
|
|
154
158
|
for interview_index, interview in enumerate(interviews):
|
155
|
-
invigilators =
|
159
|
+
invigilators = [
|
160
|
+
interview._get_invigilator(question)
|
161
|
+
for question in self.survey.questions
|
162
|
+
]
|
163
|
+
# list(interview._build_invigilators(debug=False))
|
156
164
|
for _, invigilator in enumerate(invigilators):
|
157
165
|
prompts = invigilator.get_prompts()
|
158
166
|
user_prompts.append(prompts["user_prompt"])
|
@@ -176,17 +184,27 @@ class Jobs(Base):
|
|
176
184
|
from edsl.agents.Agent import Agent
|
177
185
|
from edsl.scenarios.Scenario import Scenario
|
178
186
|
from edsl.scenarios.ScenarioList import ScenarioList
|
187
|
+
from edsl.language_models.ModelList import ModelList
|
179
188
|
|
180
189
|
if isinstance(object, Agent):
|
181
190
|
return AgentList
|
182
191
|
elif isinstance(object, Scenario):
|
183
192
|
return ScenarioList
|
193
|
+
elif isinstance(object, ModelList):
|
194
|
+
return ModelList
|
184
195
|
else:
|
185
196
|
return list
|
186
197
|
|
187
198
|
@staticmethod
|
188
199
|
def _turn_args_to_list(args):
|
189
|
-
"""Return a list of the first argument if it is a sequence, otherwise returns a list of all the arguments.
|
200
|
+
"""Return a list of the first argument if it is a sequence, otherwise returns a list of all the arguments.
|
201
|
+
|
202
|
+
Example:
|
203
|
+
|
204
|
+
>>> Jobs._turn_args_to_list([1,2,3])
|
205
|
+
[1, 2, 3]
|
206
|
+
|
207
|
+
"""
|
190
208
|
|
191
209
|
def did_user_pass_a_sequence(args):
|
192
210
|
"""Return True if the user passed a sequence, False otherwise.
|
@@ -209,7 +227,7 @@ class Jobs(Base):
|
|
209
227
|
return container_class(args)
|
210
228
|
|
211
229
|
def _get_current_objects_of_this_type(
|
212
|
-
self, object: Union[Agent, Scenario, LanguageModel]
|
230
|
+
self, object: Union["Agent", "Scenario", "LanguageModel"]
|
213
231
|
) -> tuple[list, str]:
|
214
232
|
from edsl.agents.Agent import Agent
|
215
233
|
from edsl.scenarios.Scenario import Scenario
|
@@ -292,7 +310,11 @@ class Jobs(Base):
|
|
292
310
|
|
293
311
|
@classmethod
|
294
312
|
def from_interviews(cls, interview_list):
|
295
|
-
"""Return a Jobs instance from a list of interviews.
|
313
|
+
"""Return a Jobs instance from a list of interviews.
|
314
|
+
|
315
|
+
This is useful when you have, say, a list of failed interviews and you want to create
|
316
|
+
a new job with only those interviews.
|
317
|
+
"""
|
296
318
|
survey = interview_list[0].survey
|
297
319
|
# get all the models
|
298
320
|
models = list(set([interview.model for interview in interview_list]))
|
@@ -308,6 +330,8 @@ class Jobs(Base):
|
|
308
330
|
Note that this sets the agents, model and scenarios if they have not been set. This is a side effect of the method.
|
309
331
|
This is useful because a user can create a job without setting the agents, models, or scenarios, and the job will still run,
|
310
332
|
with us filling in defaults.
|
333
|
+
|
334
|
+
|
311
335
|
"""
|
312
336
|
# if no agents, models, or scenarios are set, set them to defaults
|
313
337
|
from edsl.agents.Agent import Agent
|
@@ -319,7 +343,12 @@ class Jobs(Base):
|
|
319
343
|
self.scenarios = self.scenarios or [Scenario()]
|
320
344
|
for agent, scenario, model in product(self.agents, self.scenarios, self.models):
|
321
345
|
yield Interview(
|
322
|
-
survey=self.survey,
|
346
|
+
survey=self.survey,
|
347
|
+
agent=agent,
|
348
|
+
scenario=scenario,
|
349
|
+
model=model,
|
350
|
+
skip_retry=self.skip_retry,
|
351
|
+
raise_validation_errors=self.raise_validation_errors,
|
323
352
|
)
|
324
353
|
|
325
354
|
def create_bucket_collection(self) -> BucketCollection:
|
@@ -359,10 +388,16 @@ class Jobs(Base):
|
|
359
388
|
return links
|
360
389
|
|
361
390
|
def __hash__(self):
|
362
|
-
"""Allow the model to be used as a key in a dictionary.
|
391
|
+
"""Allow the model to be used as a key in a dictionary.
|
392
|
+
|
393
|
+
>>> from edsl.jobs import Jobs
|
394
|
+
>>> hash(Jobs.example())
|
395
|
+
846655441787442972
|
396
|
+
|
397
|
+
"""
|
363
398
|
from edsl.utilities.utilities import dict_hash
|
364
399
|
|
365
|
-
return dict_hash(self.
|
400
|
+
return dict_hash(self._to_dict())
|
366
401
|
|
367
402
|
def _output(self, message) -> None:
|
368
403
|
"""Check if a Job is verbose. If so, print the message."""
|
@@ -390,11 +425,27 @@ class Jobs(Base):
|
|
390
425
|
Traceback (most recent call last):
|
391
426
|
...
|
392
427
|
ValueError: The following parameters are in the scenarios but not in the survey: {'plop'}
|
428
|
+
|
429
|
+
>>> q = QuestionFreeText(question_text = "Hello", question_name = "ugly_question")
|
430
|
+
>>> s = Scenario({'ugly_question': "B"})
|
431
|
+
>>> j = Jobs(survey = Survey(questions=[q])).by(s)
|
432
|
+
>>> j._check_parameters()
|
433
|
+
Traceback (most recent call last):
|
434
|
+
...
|
435
|
+
ValueError: The following names are in both the survey question_names and the scenario keys: {'ugly_question'}. This will create issues.
|
393
436
|
"""
|
394
437
|
survey_parameters: set = self.survey.parameters
|
395
438
|
scenario_parameters: set = self.scenarios.parameters
|
396
439
|
|
397
|
-
msg1, msg2 = None, None
|
440
|
+
msg0, msg1, msg2 = None, None, None
|
441
|
+
|
442
|
+
# look for key issues
|
443
|
+
if intersection := set(self.scenarios.parameters) & set(
|
444
|
+
self.survey.question_names
|
445
|
+
):
|
446
|
+
msg0 = f"The following names are in both the survey question_names and the scenario keys: {intersection}. This will create issues."
|
447
|
+
|
448
|
+
raise ValueError(msg0)
|
398
449
|
|
399
450
|
if in_survey_but_not_in_scenarios := survey_parameters - scenario_parameters:
|
400
451
|
msg1 = f"The following parameters are in the survey but not in the scenarios: {in_survey_but_not_in_scenarios}"
|
@@ -409,26 +460,44 @@ class Jobs(Base):
|
|
409
460
|
if warn:
|
410
461
|
warnings.warn(message)
|
411
462
|
|
463
|
+
if self.scenarios.has_jinja_braces:
|
464
|
+
warnings.warn(
|
465
|
+
"The scenarios have Jinja braces ({{ and }}). Converting to '<<' and '>>'. If you want a different conversion, use the convert_jinja_braces method first to modify the scenario."
|
466
|
+
)
|
467
|
+
self.scenarios = self.scenarios.convert_jinja_braces()
|
468
|
+
|
469
|
+
@property
|
470
|
+
def skip_retry(self):
|
471
|
+
if not hasattr(self, "_skip_retry"):
|
472
|
+
return False
|
473
|
+
return self._skip_retry
|
474
|
+
|
475
|
+
@property
|
476
|
+
def raise_validation_errors(self):
|
477
|
+
if not hasattr(self, "_raise_validation_errors"):
|
478
|
+
return False
|
479
|
+
return self._raise_validation_errors
|
480
|
+
|
412
481
|
def run(
|
413
482
|
self,
|
414
483
|
n: int = 1,
|
415
|
-
debug: bool = False,
|
416
484
|
progress_bar: bool = False,
|
417
485
|
stop_on_exception: bool = False,
|
418
486
|
cache: Union[Cache, bool] = None,
|
419
487
|
check_api_keys: bool = False,
|
420
488
|
sidecar_model: Optional[LanguageModel] = None,
|
421
|
-
batch_mode: Optional[bool] = None,
|
422
489
|
verbose: bool = False,
|
423
490
|
print_exceptions=True,
|
424
491
|
remote_cache_description: Optional[str] = None,
|
425
492
|
remote_inference_description: Optional[str] = None,
|
493
|
+
skip_retry: bool = False,
|
494
|
+
raise_validation_errors: bool = False,
|
495
|
+
disable_remote_inference: bool = False,
|
426
496
|
) -> Results:
|
427
497
|
"""
|
428
498
|
Runs the Job: conducts Interviews and returns their results.
|
429
499
|
|
430
500
|
:param n: how many times to run each interview
|
431
|
-
:param debug: prints debug messages
|
432
501
|
:param progress_bar: shows a progress bar
|
433
502
|
:param stop_on_exception: stops the job if an exception is raised
|
434
503
|
:param cache: a cache object to store results
|
@@ -441,22 +510,22 @@ class Jobs(Base):
|
|
441
510
|
from edsl.coop.coop import Coop
|
442
511
|
|
443
512
|
self._check_parameters()
|
444
|
-
|
445
|
-
|
446
|
-
raise NotImplementedError(
|
447
|
-
"Batch mode is deprecated. Please update your code to not include 'batch_mode' in the 'run' method."
|
448
|
-
)
|
513
|
+
self._skip_retry = skip_retry
|
514
|
+
self._raise_validation_errors = raise_validation_errors
|
449
515
|
|
450
516
|
self.verbose = verbose
|
451
517
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
518
|
+
remote_cache = False
|
519
|
+
remote_inference = False
|
520
|
+
|
521
|
+
if not disable_remote_inference:
|
522
|
+
try:
|
523
|
+
coop = Coop()
|
524
|
+
user_edsl_settings = Coop().edsl_settings
|
525
|
+
remote_cache = user_edsl_settings.get("remote_caching", False)
|
526
|
+
remote_inference = user_edsl_settings.get("remote_inference", False)
|
527
|
+
except Exception:
|
528
|
+
pass
|
460
529
|
|
461
530
|
if remote_inference:
|
462
531
|
import time
|
@@ -508,7 +577,7 @@ class Jobs(Base):
|
|
508
577
|
)
|
509
578
|
return results
|
510
579
|
else:
|
511
|
-
duration =
|
580
|
+
duration = 5
|
512
581
|
time_checked = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
513
582
|
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
514
583
|
start_time = time.time()
|
@@ -533,7 +602,7 @@ class Jobs(Base):
|
|
533
602
|
)
|
534
603
|
|
535
604
|
# handle cache
|
536
|
-
if cache is None:
|
605
|
+
if cache is None or cache is True:
|
537
606
|
from edsl.data.CacheHandler import CacheHandler
|
538
607
|
|
539
608
|
cache = CacheHandler().get_cache()
|
@@ -545,12 +614,12 @@ class Jobs(Base):
|
|
545
614
|
if not remote_cache:
|
546
615
|
results = self._run_local(
|
547
616
|
n=n,
|
548
|
-
debug=debug,
|
549
617
|
progress_bar=progress_bar,
|
550
618
|
cache=cache,
|
551
619
|
stop_on_exception=stop_on_exception,
|
552
620
|
sidecar_model=sidecar_model,
|
553
621
|
print_exceptions=print_exceptions,
|
622
|
+
raise_validation_errors=raise_validation_errors,
|
554
623
|
)
|
555
624
|
|
556
625
|
results.cache = cache.new_entries_cache()
|
@@ -589,12 +658,12 @@ class Jobs(Base):
|
|
589
658
|
self._output("Running job...")
|
590
659
|
results = self._run_local(
|
591
660
|
n=n,
|
592
|
-
debug=debug,
|
593
661
|
progress_bar=progress_bar,
|
594
662
|
cache=cache,
|
595
663
|
stop_on_exception=stop_on_exception,
|
596
664
|
sidecar_model=sidecar_model,
|
597
665
|
print_exceptions=print_exceptions,
|
666
|
+
raise_validation_errors=raise_validation_errors,
|
598
667
|
)
|
599
668
|
self._output("Job completed!")
|
600
669
|
|
@@ -631,12 +700,16 @@ class Jobs(Base):
|
|
631
700
|
return results
|
632
701
|
|
633
702
|
async def run_async(self, cache=None, n=1, **kwargs):
|
634
|
-
"""Run
|
703
|
+
"""Run asynchronously."""
|
635
704
|
results = await JobsRunnerAsyncio(self).run_async(cache=cache, n=n, **kwargs)
|
636
705
|
return results
|
637
706
|
|
638
707
|
def all_question_parameters(self):
|
639
|
-
"""Return all the fields in the questions in the survey.
|
708
|
+
"""Return all the fields in the questions in the survey.
|
709
|
+
>>> from edsl.jobs import Jobs
|
710
|
+
>>> Jobs.example().all_question_parameters()
|
711
|
+
{'period'}
|
712
|
+
"""
|
640
713
|
return set.union(*[question.parameters for question in self.survey.questions])
|
641
714
|
|
642
715
|
#######################
|
@@ -677,15 +750,19 @@ class Jobs(Base):
|
|
677
750
|
#######################
|
678
751
|
# Serialization methods
|
679
752
|
#######################
|
753
|
+
|
754
|
+
def _to_dict(self):
|
755
|
+
return {
|
756
|
+
"survey": self.survey._to_dict(),
|
757
|
+
"agents": [agent._to_dict() for agent in self.agents],
|
758
|
+
"models": [model._to_dict() for model in self.models],
|
759
|
+
"scenarios": [scenario._to_dict() for scenario in self.scenarios],
|
760
|
+
}
|
761
|
+
|
680
762
|
@add_edsl_version
|
681
763
|
def to_dict(self) -> dict:
|
682
764
|
"""Convert the Jobs instance to a dictionary."""
|
683
|
-
return
|
684
|
-
"survey": self.survey.to_dict(),
|
685
|
-
"agents": [agent.to_dict() for agent in self.agents],
|
686
|
-
"models": [model.to_dict() for model in self.models],
|
687
|
-
"scenarios": [scenario.to_dict() for scenario in self.scenarios],
|
688
|
-
}
|
765
|
+
return self._to_dict()
|
689
766
|
|
690
767
|
@classmethod
|
691
768
|
@remove_edsl_version
|
@@ -704,7 +781,13 @@ class Jobs(Base):
|
|
704
781
|
)
|
705
782
|
|
706
783
|
def __eq__(self, other: Jobs) -> bool:
|
707
|
-
"""Return True if the Jobs instance is equal to another Jobs instance.
|
784
|
+
"""Return True if the Jobs instance is equal to another Jobs instance.
|
785
|
+
|
786
|
+
>>> from edsl.jobs import Jobs
|
787
|
+
>>> Jobs.example() == Jobs.example()
|
788
|
+
True
|
789
|
+
|
790
|
+
"""
|
708
791
|
return self.to_dict() == other.to_dict()
|
709
792
|
|
710
793
|
#######################
|
@@ -712,11 +795,16 @@ class Jobs(Base):
|
|
712
795
|
#######################
|
713
796
|
@classmethod
|
714
797
|
def example(
|
715
|
-
cls,
|
798
|
+
cls,
|
799
|
+
throw_exception_probability: float = 0.0,
|
800
|
+
randomize: bool = False,
|
801
|
+
test_model=False,
|
716
802
|
) -> Jobs:
|
717
803
|
"""Return an example Jobs instance.
|
718
804
|
|
719
805
|
:param throw_exception_probability: the probability that an exception will be thrown when answering a question. This is useful for testing error handling.
|
806
|
+
:param randomize: whether to randomize the job by adding a random string to the period
|
807
|
+
:param test_model: whether to use a test model
|
720
808
|
|
721
809
|
>>> Jobs.example()
|
722
810
|
Jobs(...)
|
@@ -730,6 +818,11 @@ class Jobs(Base):
|
|
730
818
|
|
731
819
|
addition = "" if not randomize else str(uuid4())
|
732
820
|
|
821
|
+
if test_model:
|
822
|
+
from edsl.language_models import LanguageModel
|
823
|
+
|
824
|
+
m = LanguageModel.example(test_model=True)
|
825
|
+
|
733
826
|
# (status, question, period)
|
734
827
|
agent_answers = {
|
735
828
|
("Joyful", "how_feeling", "morning"): "OK",
|
@@ -777,7 +870,10 @@ class Jobs(Base):
|
|
777
870
|
Scenario({"period": "afternoon"}),
|
778
871
|
]
|
779
872
|
)
|
780
|
-
|
873
|
+
if test_model:
|
874
|
+
job = base_survey.by(m).by(scenario_list).by(joy_agent, sad_agent)
|
875
|
+
else:
|
876
|
+
job = base_survey.by(scenario_list).by(joy_agent, sad_agent)
|
781
877
|
|
782
878
|
return job
|
783
879
|
|
@@ -802,7 +898,7 @@ def main():
|
|
802
898
|
|
803
899
|
job = Jobs.example()
|
804
900
|
len(job) == 8
|
805
|
-
results = job.run(
|
901
|
+
results = job.run(cache=Cache())
|
806
902
|
len(results) == 8
|
807
903
|
results
|
808
904
|
|
@@ -13,6 +13,8 @@ class BucketCollection(UserDict):
|
|
13
13
|
def __init__(self, infinity_buckets=False):
|
14
14
|
super().__init__()
|
15
15
|
self.infinity_buckets = infinity_buckets
|
16
|
+
self.models_to_services = {}
|
17
|
+
self.services_to_buckets = {}
|
16
18
|
|
17
19
|
def __repr__(self):
|
18
20
|
return f"BucketCollection({self.data})"
|
@@ -21,6 +23,7 @@ class BucketCollection(UserDict):
|
|
21
23
|
"""Adds a model to the bucket collection.
|
22
24
|
|
23
25
|
This will create the token and request buckets for the model."""
|
26
|
+
|
24
27
|
# compute the TPS and RPS from the model
|
25
28
|
if not self.infinity_buckets:
|
26
29
|
TPS = model.TPM / 60.0
|
@@ -29,22 +32,28 @@ class BucketCollection(UserDict):
|
|
29
32
|
TPS = float("inf")
|
30
33
|
RPS = float("inf")
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
35
|
+
if model.model not in self.models_to_services:
|
36
|
+
service = model._inference_service_
|
37
|
+
if service not in self.services_to_buckets:
|
38
|
+
requests_bucket = TokenBucket(
|
39
|
+
bucket_name=service,
|
40
|
+
bucket_type="requests",
|
41
|
+
capacity=RPS,
|
42
|
+
refill_rate=RPS,
|
43
|
+
)
|
44
|
+
tokens_bucket = TokenBucket(
|
45
|
+
bucket_name=service,
|
46
|
+
bucket_type="tokens",
|
47
|
+
capacity=TPS,
|
48
|
+
refill_rate=TPS,
|
49
|
+
)
|
50
|
+
self.services_to_buckets[service] = ModelBuckets(
|
51
|
+
requests_bucket, tokens_bucket
|
52
|
+
)
|
53
|
+
self.models_to_services[model.model] = service
|
54
|
+
self[model] = self.services_to_buckets[service]
|
46
55
|
else:
|
47
|
-
self[model] =
|
56
|
+
self[model] = self.services_to_buckets[self.models_to_services[model.model]]
|
48
57
|
|
49
58
|
def visualize(self) -> dict:
|
50
59
|
"""Visualize the token and request buckets for each model."""
|