edsl 0.1.32__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.
Files changed (181) hide show
  1. edsl/Base.py +9 -3
  2. edsl/TemplateLoader.py +24 -0
  3. edsl/__init__.py +8 -3
  4. edsl/__version__.py +1 -1
  5. edsl/agents/Agent.py +40 -8
  6. edsl/agents/AgentList.py +43 -0
  7. edsl/agents/Invigilator.py +135 -219
  8. edsl/agents/InvigilatorBase.py +148 -59
  9. edsl/agents/{PromptConstructionMixin.py → PromptConstructor.py} +138 -89
  10. edsl/agents/__init__.py +1 -0
  11. edsl/auto/AutoStudy.py +117 -0
  12. edsl/auto/StageBase.py +230 -0
  13. edsl/auto/StageGenerateSurvey.py +178 -0
  14. edsl/auto/StageLabelQuestions.py +125 -0
  15. edsl/auto/StagePersona.py +61 -0
  16. edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
  17. edsl/auto/StagePersonaDimensionValues.py +74 -0
  18. edsl/auto/StagePersonaDimensions.py +69 -0
  19. edsl/auto/StageQuestions.py +73 -0
  20. edsl/auto/SurveyCreatorPipeline.py +21 -0
  21. edsl/auto/utilities.py +224 -0
  22. edsl/config.py +47 -56
  23. edsl/coop/PriceFetcher.py +58 -0
  24. edsl/coop/coop.py +50 -7
  25. edsl/data/Cache.py +35 -1
  26. edsl/data_transfer_models.py +73 -38
  27. edsl/enums.py +4 -0
  28. edsl/exceptions/language_models.py +25 -1
  29. edsl/exceptions/questions.py +62 -5
  30. edsl/exceptions/results.py +4 -0
  31. edsl/inference_services/AnthropicService.py +13 -11
  32. edsl/inference_services/AwsBedrock.py +19 -17
  33. edsl/inference_services/AzureAI.py +37 -20
  34. edsl/inference_services/GoogleService.py +16 -12
  35. edsl/inference_services/GroqService.py +2 -0
  36. edsl/inference_services/InferenceServiceABC.py +58 -3
  37. edsl/inference_services/MistralAIService.py +120 -0
  38. edsl/inference_services/OpenAIService.py +48 -54
  39. edsl/inference_services/TestService.py +80 -0
  40. edsl/inference_services/TogetherAIService.py +170 -0
  41. edsl/inference_services/models_available_cache.py +0 -6
  42. edsl/inference_services/registry.py +6 -0
  43. edsl/jobs/Answers.py +10 -12
  44. edsl/jobs/FailedQuestion.py +78 -0
  45. edsl/jobs/Jobs.py +37 -22
  46. edsl/jobs/buckets/BucketCollection.py +24 -15
  47. edsl/jobs/buckets/TokenBucket.py +93 -14
  48. edsl/jobs/interviews/Interview.py +366 -78
  49. edsl/jobs/interviews/{interview_exception_tracking.py → InterviewExceptionCollection.py} +14 -68
  50. edsl/jobs/interviews/InterviewExceptionEntry.py +85 -19
  51. edsl/jobs/runners/JobsRunnerAsyncio.py +146 -175
  52. edsl/jobs/runners/JobsRunnerStatus.py +331 -0
  53. edsl/jobs/tasks/QuestionTaskCreator.py +30 -23
  54. edsl/jobs/tasks/TaskHistory.py +148 -213
  55. edsl/language_models/LanguageModel.py +261 -156
  56. edsl/language_models/ModelList.py +2 -2
  57. edsl/language_models/RegisterLanguageModelsMeta.py +14 -29
  58. edsl/language_models/fake_openai_call.py +15 -0
  59. edsl/language_models/fake_openai_service.py +61 -0
  60. edsl/language_models/registry.py +23 -6
  61. edsl/language_models/repair.py +0 -19
  62. edsl/language_models/utilities.py +61 -0
  63. edsl/notebooks/Notebook.py +20 -2
  64. edsl/prompts/Prompt.py +52 -2
  65. edsl/questions/AnswerValidatorMixin.py +23 -26
  66. edsl/questions/QuestionBase.py +330 -249
  67. edsl/questions/QuestionBaseGenMixin.py +133 -0
  68. edsl/questions/QuestionBasePromptsMixin.py +266 -0
  69. edsl/questions/QuestionBudget.py +99 -41
  70. edsl/questions/QuestionCheckBox.py +227 -35
  71. edsl/questions/QuestionExtract.py +98 -27
  72. edsl/questions/QuestionFreeText.py +52 -29
  73. edsl/questions/QuestionFunctional.py +7 -0
  74. edsl/questions/QuestionList.py +141 -22
  75. edsl/questions/QuestionMultipleChoice.py +159 -65
  76. edsl/questions/QuestionNumerical.py +88 -46
  77. edsl/questions/QuestionRank.py +182 -24
  78. edsl/questions/Quick.py +41 -0
  79. edsl/questions/RegisterQuestionsMeta.py +31 -12
  80. edsl/questions/ResponseValidatorABC.py +170 -0
  81. edsl/questions/__init__.py +3 -4
  82. edsl/questions/decorators.py +21 -0
  83. edsl/questions/derived/QuestionLikertFive.py +10 -5
  84. edsl/questions/derived/QuestionLinearScale.py +15 -2
  85. edsl/questions/derived/QuestionTopK.py +10 -1
  86. edsl/questions/derived/QuestionYesNo.py +24 -3
  87. edsl/questions/descriptors.py +43 -7
  88. edsl/questions/prompt_templates/question_budget.jinja +13 -0
  89. edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
  90. edsl/questions/prompt_templates/question_extract.jinja +11 -0
  91. edsl/questions/prompt_templates/question_free_text.jinja +3 -0
  92. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
  93. edsl/questions/prompt_templates/question_list.jinja +17 -0
  94. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
  95. edsl/questions/prompt_templates/question_numerical.jinja +37 -0
  96. edsl/questions/question_registry.py +6 -2
  97. edsl/questions/templates/__init__.py +0 -0
  98. edsl/questions/templates/budget/__init__.py +0 -0
  99. edsl/questions/templates/budget/answering_instructions.jinja +7 -0
  100. edsl/questions/templates/budget/question_presentation.jinja +7 -0
  101. edsl/questions/templates/checkbox/__init__.py +0 -0
  102. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
  103. edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
  104. edsl/questions/templates/extract/__init__.py +0 -0
  105. edsl/questions/templates/extract/answering_instructions.jinja +7 -0
  106. edsl/questions/templates/extract/question_presentation.jinja +1 -0
  107. edsl/questions/templates/free_text/__init__.py +0 -0
  108. edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  109. edsl/questions/templates/free_text/question_presentation.jinja +1 -0
  110. edsl/questions/templates/likert_five/__init__.py +0 -0
  111. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
  112. edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
  113. edsl/questions/templates/linear_scale/__init__.py +0 -0
  114. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
  115. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
  116. edsl/questions/templates/list/__init__.py +0 -0
  117. edsl/questions/templates/list/answering_instructions.jinja +4 -0
  118. edsl/questions/templates/list/question_presentation.jinja +5 -0
  119. edsl/questions/templates/multiple_choice/__init__.py +0 -0
  120. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
  121. edsl/questions/templates/multiple_choice/html.jinja +0 -0
  122. edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
  123. edsl/questions/templates/numerical/__init__.py +0 -0
  124. edsl/questions/templates/numerical/answering_instructions.jinja +8 -0
  125. edsl/questions/templates/numerical/question_presentation.jinja +7 -0
  126. edsl/questions/templates/rank/__init__.py +0 -0
  127. edsl/questions/templates/rank/answering_instructions.jinja +11 -0
  128. edsl/questions/templates/rank/question_presentation.jinja +15 -0
  129. edsl/questions/templates/top_k/__init__.py +0 -0
  130. edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
  131. edsl/questions/templates/top_k/question_presentation.jinja +22 -0
  132. edsl/questions/templates/yes_no/__init__.py +0 -0
  133. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
  134. edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
  135. edsl/results/Dataset.py +20 -0
  136. edsl/results/DatasetExportMixin.py +46 -48
  137. edsl/results/DatasetTree.py +145 -0
  138. edsl/results/Result.py +32 -5
  139. edsl/results/Results.py +135 -46
  140. edsl/results/ResultsDBMixin.py +3 -3
  141. edsl/results/Selector.py +118 -0
  142. edsl/results/tree_explore.py +115 -0
  143. edsl/scenarios/FileStore.py +71 -10
  144. edsl/scenarios/Scenario.py +96 -25
  145. edsl/scenarios/ScenarioImageMixin.py +2 -2
  146. edsl/scenarios/ScenarioList.py +361 -39
  147. edsl/scenarios/ScenarioListExportMixin.py +9 -0
  148. edsl/scenarios/ScenarioListPdfMixin.py +150 -4
  149. edsl/study/SnapShot.py +8 -1
  150. edsl/study/Study.py +32 -0
  151. edsl/surveys/Rule.py +10 -1
  152. edsl/surveys/RuleCollection.py +21 -5
  153. edsl/surveys/Survey.py +637 -311
  154. edsl/surveys/SurveyExportMixin.py +71 -9
  155. edsl/surveys/SurveyFlowVisualizationMixin.py +2 -1
  156. edsl/surveys/SurveyQualtricsImport.py +75 -4
  157. edsl/surveys/instructions/ChangeInstruction.py +47 -0
  158. edsl/surveys/instructions/Instruction.py +34 -0
  159. edsl/surveys/instructions/InstructionCollection.py +77 -0
  160. edsl/surveys/instructions/__init__.py +0 -0
  161. edsl/templates/error_reporting/base.html +24 -0
  162. edsl/templates/error_reporting/exceptions_by_model.html +35 -0
  163. edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
  164. edsl/templates/error_reporting/exceptions_by_type.html +17 -0
  165. edsl/templates/error_reporting/interview_details.html +116 -0
  166. edsl/templates/error_reporting/interviews.html +10 -0
  167. edsl/templates/error_reporting/overview.html +5 -0
  168. edsl/templates/error_reporting/performance_plot.html +2 -0
  169. edsl/templates/error_reporting/report.css +74 -0
  170. edsl/templates/error_reporting/report.html +118 -0
  171. edsl/templates/error_reporting/report.js +25 -0
  172. edsl/utilities/utilities.py +9 -1
  173. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/METADATA +5 -2
  174. edsl-0.1.33.dist-info/RECORD +295 -0
  175. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +0 -286
  176. edsl/jobs/interviews/retry_management.py +0 -37
  177. edsl/jobs/runners/JobsRunnerStatusMixin.py +0 -333
  178. edsl/utilities/gcp_bucket/simple_example.py +0 -9
  179. edsl-0.1.32.dist-info/RECORD +0 -209
  180. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/LICENSE +0 -0
  181. {edsl-0.1.32.dist-info → edsl-0.1.33.dist-info}/WHEEL +0 -0
@@ -0,0 +1,80 @@
1
+ from typing import Any, List
2
+ import os
3
+ import asyncio
4
+ from edsl.inference_services.InferenceServiceABC import InferenceServiceABC
5
+ from edsl.language_models import LanguageModel
6
+ from edsl.inference_services.rate_limits_cache import rate_limits
7
+ from edsl.utilities.utilities import fix_partial_correct_response
8
+
9
+ from edsl.enums import InferenceServiceType
10
+ import random
11
+
12
+
13
+ class TestService(InferenceServiceABC):
14
+ """OpenAI service class."""
15
+
16
+ _inference_service_ = "test"
17
+ _env_key_name_ = None
18
+ _base_url_ = None
19
+
20
+ _sync_client_ = None
21
+ _async_client_ = None
22
+
23
+ _sync_client_instance = None
24
+ _async_client_instance = None
25
+
26
+ key_sequence = None
27
+ usage_sequence = None
28
+ model_exclude_list = []
29
+ input_token_name = "prompt_tokens"
30
+ output_token_name = "completion_tokens"
31
+
32
+ @classmethod
33
+ def available(cls) -> list[str]:
34
+ return ["test"]
35
+
36
+ @classmethod
37
+ def create_model(cls, model_name, model_class_name=None) -> LanguageModel:
38
+ throw_exception = False
39
+
40
+ class TestServiceLanguageModel(LanguageModel):
41
+ _model_ = "test"
42
+ _parameters_ = {"temperature": 0.5}
43
+ _inference_service_ = InferenceServiceType.TEST.value
44
+ usage_sequence = ["usage"]
45
+ key_sequence = ["message", 0, "text"]
46
+ input_token_name = cls.input_token_name
47
+ output_token_name = cls.output_token_name
48
+ _rpm = 1000
49
+ _tpm = 100000
50
+
51
+ @property
52
+ def _canned_response(self):
53
+ if hasattr(self, "canned_response"):
54
+ return self.canned_response
55
+ else:
56
+ return "Hello, world"
57
+
58
+ async def async_execute_model_call(
59
+ self,
60
+ user_prompt: str,
61
+ system_prompt: str,
62
+ encoded_image=None,
63
+ ) -> dict[str, Any]:
64
+ await asyncio.sleep(0.1)
65
+ # return {"message": """{"answer": "Hello, world"}"""}
66
+
67
+ if hasattr(self, "throw_exception") and self.throw_exception:
68
+ if hasattr(self, "exception_probability"):
69
+ p = self.exception_probability
70
+ else:
71
+ p = 1
72
+
73
+ if random.random() < p:
74
+ raise Exception("This is a test error")
75
+ return {
76
+ "message": [{"text": f"{self._canned_response}"}],
77
+ "usage": {"prompt_tokens": 1, "completion_tokens": 1},
78
+ }
79
+
80
+ return TestServiceLanguageModel
@@ -0,0 +1,170 @@
1
+ import aiohttp
2
+ import json
3
+ import requests
4
+ from typing import Any, List
5
+
6
+ # from edsl.inference_services.InferenceServiceABC import InferenceServiceABC
7
+ from edsl.language_models import LanguageModel
8
+
9
+ from edsl.inference_services.OpenAIService import OpenAIService
10
+ import openai
11
+
12
+
13
+ class TogetherAIService(OpenAIService):
14
+ """DeepInfra service class."""
15
+
16
+ _inference_service_ = "together"
17
+ _env_key_name_ = "TOGETHER_API_KEY"
18
+ _base_url_ = "https://api.together.xyz/v1"
19
+ _models_list_cache: List[str] = []
20
+
21
+ # These are non-serverless models. There was no api param to filter them
22
+ model_exclude_list = [
23
+ "EleutherAI/llemma_7b",
24
+ "HuggingFaceH4/zephyr-7b-beta",
25
+ "Nexusflow/NexusRaven-V2-13B",
26
+ "NousResearch/Hermes-2-Theta-Llama-3-70B",
27
+ "NousResearch/Nous-Capybara-7B-V1p9",
28
+ "NousResearch/Nous-Hermes-13b",
29
+ "NousResearch/Nous-Hermes-2-Mistral-7B-DPO",
30
+ "NousResearch/Nous-Hermes-2-Mixtral-8x7B-SFT",
31
+ "NousResearch/Nous-Hermes-Llama2-13b",
32
+ "NousResearch/Nous-Hermes-Llama2-70b",
33
+ "NousResearch/Nous-Hermes-llama-2-7b",
34
+ "NumbersStation/nsql-llama-2-7B",
35
+ "Open-Orca/Mistral-7B-OpenOrca",
36
+ "Phind/Phind-CodeLlama-34B-Python-v1",
37
+ "Phind/Phind-CodeLlama-34B-v2",
38
+ "Qwen/Qwen1.5-0.5B",
39
+ "Qwen/Qwen1.5-0.5B-Chat",
40
+ "Qwen/Qwen1.5-1.8B",
41
+ "Qwen/Qwen1.5-1.8B-Chat",
42
+ "Qwen/Qwen1.5-14B",
43
+ "Qwen/Qwen1.5-14B-Chat",
44
+ "Qwen/Qwen1.5-32B",
45
+ "Qwen/Qwen1.5-32B-Chat",
46
+ "Qwen/Qwen1.5-4B",
47
+ "Qwen/Qwen1.5-4B-Chat",
48
+ "Qwen/Qwen1.5-72B",
49
+ "Qwen/Qwen1.5-7B",
50
+ "Qwen/Qwen1.5-7B-Chat",
51
+ "Qwen/Qwen2-1.5B",
52
+ "Qwen/Qwen2-1.5B-Instruct",
53
+ "Qwen/Qwen2-72B",
54
+ "Qwen/Qwen2-7B",
55
+ "Qwen/Qwen2-7B-Instruct",
56
+ "SG161222/Realistic_Vision_V3.0_VAE",
57
+ "Snowflake/snowflake-arctic-instruct",
58
+ "Undi95/ReMM-SLERP-L2-13B",
59
+ "Undi95/Toppy-M-7B",
60
+ "WizardLM/WizardCoder-Python-34B-V1.0",
61
+ "WizardLM/WizardLM-13B-V1.2",
62
+ "WizardLM/WizardLM-70B-V1.0",
63
+ "allenai/OLMo-7B",
64
+ "allenai/OLMo-7B-Instruct",
65
+ "bert-base-uncased",
66
+ "codellama/CodeLlama-13b-Instruct-hf",
67
+ "codellama/CodeLlama-13b-Python-hf",
68
+ "codellama/CodeLlama-13b-hf",
69
+ "codellama/CodeLlama-34b-Python-hf",
70
+ "codellama/CodeLlama-34b-hf",
71
+ "codellama/CodeLlama-70b-Instruct-hf",
72
+ "codellama/CodeLlama-70b-Python-hf",
73
+ "codellama/CodeLlama-70b-hf",
74
+ "codellama/CodeLlama-7b-Instruct-hf",
75
+ "codellama/CodeLlama-7b-Python-hf",
76
+ "codellama/CodeLlama-7b-hf",
77
+ "cognitivecomputations/dolphin-2.5-mixtral-8x7b",
78
+ "deepseek-ai/deepseek-coder-33b-instruct",
79
+ "garage-bAInd/Platypus2-70B-instruct",
80
+ "google/gemma-2b",
81
+ "google/gemma-7b",
82
+ "google/gemma-7b-it",
83
+ "gradientai/Llama-3-70B-Instruct-Gradient-1048k",
84
+ "hazyresearch/M2-BERT-2k-Retrieval-Encoder-V1",
85
+ "huggyllama/llama-13b",
86
+ "huggyllama/llama-30b",
87
+ "huggyllama/llama-65b",
88
+ "huggyllama/llama-7b",
89
+ "lmsys/vicuna-13b-v1.3",
90
+ "lmsys/vicuna-13b-v1.5",
91
+ "lmsys/vicuna-13b-v1.5-16k",
92
+ "lmsys/vicuna-7b-v1.3",
93
+ "lmsys/vicuna-7b-v1.5",
94
+ "meta-llama/Llama-2-13b-hf",
95
+ "meta-llama/Llama-2-70b-chat-hf",
96
+ "meta-llama/Llama-2-7b-hf",
97
+ "meta-llama/Llama-3-70b-hf",
98
+ "meta-llama/Llama-3-8b-hf",
99
+ "meta-llama/Meta-Llama-3-70B",
100
+ "meta-llama/Meta-Llama-3-70B-Instruct",
101
+ "meta-llama/Meta-Llama-3-8B-Instruct",
102
+ "meta-llama/Meta-Llama-3.1-70B-Instruct-Reference",
103
+ "meta-llama/Meta-Llama-3.1-70B-Reference",
104
+ "meta-llama/Meta-Llama-3.1-8B-Reference",
105
+ "microsoft/phi-2",
106
+ "mistralai/Mixtral-8x22B",
107
+ "openchat/openchat-3.5-1210",
108
+ "prompthero/openjourney",
109
+ "runwayml/stable-diffusion-v1-5",
110
+ "sentence-transformers/msmarco-bert-base-dot-v5",
111
+ "snorkelai/Snorkel-Mistral-PairRM-DPO",
112
+ "stabilityai/stable-diffusion-2-1",
113
+ "teknium/OpenHermes-2-Mistral-7B",
114
+ "teknium/OpenHermes-2p5-Mistral-7B",
115
+ "togethercomputer/CodeLlama-13b-Instruct",
116
+ "togethercomputer/CodeLlama-13b-Python",
117
+ "togethercomputer/CodeLlama-34b",
118
+ "togethercomputer/CodeLlama-34b-Python",
119
+ "togethercomputer/CodeLlama-7b-Instruct",
120
+ "togethercomputer/CodeLlama-7b-Python",
121
+ "togethercomputer/Koala-13B",
122
+ "togethercomputer/Koala-7B",
123
+ "togethercomputer/LLaMA-2-7B-32K",
124
+ "togethercomputer/SOLAR-10.7B-Instruct-v1.0-int4",
125
+ "togethercomputer/StripedHyena-Hessian-7B",
126
+ "togethercomputer/alpaca-7b",
127
+ "togethercomputer/evo-1-131k-base",
128
+ "togethercomputer/evo-1-8k-base",
129
+ "togethercomputer/guanaco-13b",
130
+ "togethercomputer/guanaco-33b",
131
+ "togethercomputer/guanaco-65b",
132
+ "togethercomputer/guanaco-7b",
133
+ "togethercomputer/llama-2-13b",
134
+ "togethercomputer/llama-2-70b-chat",
135
+ "togethercomputer/llama-2-7b",
136
+ "wavymulder/Analog-Diffusion",
137
+ "zero-one-ai/Yi-34B",
138
+ "zero-one-ai/Yi-34B-Chat",
139
+ "zero-one-ai/Yi-6B",
140
+ ]
141
+
142
+ _sync_client_ = openai.OpenAI
143
+ _async_client_ = openai.AsyncOpenAI
144
+
145
+ @classmethod
146
+ def get_model_list(cls):
147
+ # Togheter.ai has a different response in model list then openai
148
+ # and the OpenAI class returns an error when calling .models.list()
149
+ import requests
150
+ import os
151
+
152
+ url = "https://api.together.xyz/v1/models?filter=serverless"
153
+ token = os.getenv(cls._env_key_name_)
154
+ headers = {"accept": "application/json", "authorization": f"Bearer {token}"}
155
+
156
+ response = requests.get(url, headers=headers)
157
+ return response.json()
158
+
159
+ @classmethod
160
+ def available(cls) -> List[str]:
161
+ if not cls._models_list_cache:
162
+ try:
163
+ cls._models_list_cache = [
164
+ m["id"]
165
+ for m in cls.get_model_list()
166
+ if m["id"] not in cls.model_exclude_list
167
+ ]
168
+ except Exception as e:
169
+ raise
170
+ return cls._models_list_cache
@@ -70,12 +70,6 @@ models_available = {
70
70
  "amazon.titan-tg1-large",
71
71
  "amazon.titan-text-lite-v1",
72
72
  "amazon.titan-text-express-v1",
73
- "ai21.j2-grande-instruct",
74
- "ai21.j2-jumbo-instruct",
75
- "ai21.j2-mid",
76
- "ai21.j2-mid-v1",
77
- "ai21.j2-ultra",
78
- "ai21.j2-ultra-v1",
79
73
  "anthropic.claude-instant-v1",
80
74
  "anthropic.claude-v2:1",
81
75
  "anthropic.claude-v2",
@@ -10,6 +10,9 @@ from edsl.inference_services.GroqService import GroqService
10
10
  from edsl.inference_services.AwsBedrock import AwsBedrockService
11
11
  from edsl.inference_services.AzureAI import AzureAIService
12
12
  from edsl.inference_services.OllamaService import OllamaService
13
+ from edsl.inference_services.TestService import TestService
14
+ from edsl.inference_services.MistralAIService import MistralAIService
15
+ from edsl.inference_services.TogetherAIService import TogetherAIService
13
16
 
14
17
  default = InferenceServicesCollection(
15
18
  [
@@ -21,5 +24,8 @@ default = InferenceServicesCollection(
21
24
  AwsBedrockService,
22
25
  AzureAIService,
23
26
  OllamaService,
27
+ TestService,
28
+ MistralAIService,
29
+ TogetherAIService,
24
30
  ]
25
31
  )
edsl/jobs/Answers.py CHANGED
@@ -2,24 +2,22 @@
2
2
 
3
3
  from collections import UserDict
4
4
  from rich.table import Table
5
+ from edsl.data_transfer_models import EDSLResultObjectInput
5
6
 
6
7
 
7
8
  class Answers(UserDict):
8
9
  """Helper class to hold the answers to a survey."""
9
10
 
10
- def add_answer(self, response, question) -> None:
11
- """Add a response to the answers dictionary.
12
-
13
- >>> from edsl import QuestionFreeText
14
- >>> q = QuestionFreeText.example()
15
- >>> answers = Answers()
16
- >>> answers.add_answer({"answer": "yes"}, q)
17
- >>> answers[q.question_name]
18
- 'yes'
19
- """
20
- answer = response.get("answer")
21
- comment = response.pop("comment", None)
11
+ def add_answer(
12
+ self, response: EDSLResultObjectInput, question: "QuestionBase"
13
+ ) -> None:
14
+ """Add a response to the answers dictionary."""
15
+ answer = response.answer
16
+ comment = response.comment
17
+ generated_tokens = response.generated_tokens
22
18
  # record the answer
19
+ if generated_tokens:
20
+ self[question.question_name + "_generated_tokens"] = generated_tokens
23
21
  self[question.question_name] = answer
24
22
  if comment:
25
23
  self[question.question_name + "_comment"] = comment
@@ -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
@@ -156,7 +156,11 @@ class Jobs(Base):
156
156
  from edsl.results.Dataset import Dataset
157
157
 
158
158
  for interview_index, interview in enumerate(interviews):
159
- invigilators = list(interview._build_invigilators(debug=False))
159
+ invigilators = [
160
+ interview._get_invigilator(question)
161
+ for question in self.survey.questions
162
+ ]
163
+ # list(interview._build_invigilators(debug=False))
160
164
  for _, invigilator in enumerate(invigilators):
161
165
  prompts = invigilator.get_prompts()
162
166
  user_prompts.append(prompts["user_prompt"])
@@ -344,6 +348,7 @@ class Jobs(Base):
344
348
  scenario=scenario,
345
349
  model=model,
346
350
  skip_retry=self.skip_retry,
351
+ raise_validation_errors=self.raise_validation_errors,
347
352
  )
348
353
 
349
354
  def create_bucket_collection(self) -> BucketCollection:
@@ -455,33 +460,44 @@ class Jobs(Base):
455
460
  if warn:
456
461
  warnings.warn(message)
457
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
+
458
469
  @property
459
470
  def skip_retry(self):
460
471
  if not hasattr(self, "_skip_retry"):
461
472
  return False
462
473
  return self._skip_retry
463
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
+
464
481
  def run(
465
482
  self,
466
483
  n: int = 1,
467
- debug: bool = False,
468
484
  progress_bar: bool = False,
469
485
  stop_on_exception: bool = False,
470
486
  cache: Union[Cache, bool] = None,
471
487
  check_api_keys: bool = False,
472
488
  sidecar_model: Optional[LanguageModel] = None,
473
- batch_mode: Optional[bool] = None,
474
489
  verbose: bool = False,
475
490
  print_exceptions=True,
476
491
  remote_cache_description: Optional[str] = None,
477
492
  remote_inference_description: Optional[str] = None,
478
493
  skip_retry: bool = False,
494
+ raise_validation_errors: bool = False,
495
+ disable_remote_inference: bool = False,
479
496
  ) -> Results:
480
497
  """
481
498
  Runs the Job: conducts Interviews and returns their results.
482
499
 
483
500
  :param n: how many times to run each interview
484
- :param debug: prints debug messages
485
501
  :param progress_bar: shows a progress bar
486
502
  :param stop_on_exception: stops the job if an exception is raised
487
503
  :param cache: a cache object to store results
@@ -495,22 +511,21 @@ class Jobs(Base):
495
511
 
496
512
  self._check_parameters()
497
513
  self._skip_retry = skip_retry
498
-
499
- if batch_mode is not None:
500
- raise NotImplementedError(
501
- "Batch mode is deprecated. Please update your code to not include 'batch_mode' in the 'run' method."
502
- )
514
+ self._raise_validation_errors = raise_validation_errors
503
515
 
504
516
  self.verbose = verbose
505
517
 
506
- try:
507
- coop = Coop()
508
- user_edsl_settings = coop.edsl_settings
509
- remote_cache = user_edsl_settings["remote_caching"]
510
- remote_inference = user_edsl_settings["remote_inference"]
511
- except Exception:
512
- remote_cache = False
513
- remote_inference = False
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
514
529
 
515
530
  if remote_inference:
516
531
  import time
@@ -562,7 +577,7 @@ class Jobs(Base):
562
577
  )
563
578
  return results
564
579
  else:
565
- duration = 10 if len(self) < 10 else 60
580
+ duration = 5
566
581
  time_checked = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
567
582
  frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
568
583
  start_time = time.time()
@@ -587,7 +602,7 @@ class Jobs(Base):
587
602
  )
588
603
 
589
604
  # handle cache
590
- if cache is None:
605
+ if cache is None or cache is True:
591
606
  from edsl.data.CacheHandler import CacheHandler
592
607
 
593
608
  cache = CacheHandler().get_cache()
@@ -599,12 +614,12 @@ class Jobs(Base):
599
614
  if not remote_cache:
600
615
  results = self._run_local(
601
616
  n=n,
602
- debug=debug,
603
617
  progress_bar=progress_bar,
604
618
  cache=cache,
605
619
  stop_on_exception=stop_on_exception,
606
620
  sidecar_model=sidecar_model,
607
621
  print_exceptions=print_exceptions,
622
+ raise_validation_errors=raise_validation_errors,
608
623
  )
609
624
 
610
625
  results.cache = cache.new_entries_cache()
@@ -643,12 +658,12 @@ class Jobs(Base):
643
658
  self._output("Running job...")
644
659
  results = self._run_local(
645
660
  n=n,
646
- debug=debug,
647
661
  progress_bar=progress_bar,
648
662
  cache=cache,
649
663
  stop_on_exception=stop_on_exception,
650
664
  sidecar_model=sidecar_model,
651
665
  print_exceptions=print_exceptions,
666
+ raise_validation_errors=raise_validation_errors,
652
667
  )
653
668
  self._output("Job completed!")
654
669
 
@@ -883,7 +898,7 @@ def main():
883
898
 
884
899
  job = Jobs.example()
885
900
  len(job) == 8
886
- results = job.run(debug=True, cache=Cache())
901
+ results = job.run(cache=Cache())
887
902
  len(results) == 8
888
903
  results
889
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
- # create the buckets
33
- requests_bucket = TokenBucket(
34
- bucket_name=model.model,
35
- bucket_type="requests",
36
- capacity=RPS,
37
- refill_rate=RPS,
38
- )
39
- tokens_bucket = TokenBucket(
40
- bucket_name=model.model, bucket_type="tokens", capacity=TPS, refill_rate=TPS
41
- )
42
- model_buckets = ModelBuckets(requests_bucket, tokens_bucket)
43
- if model in self:
44
- # it if already exists, combine the buckets
45
- self[model] += model_buckets
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] = model_buckets
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."""