edsl 0.1.33__py3-none-any.whl → 0.1.33.dev2__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 +3 -9
- edsl/__init__.py +0 -1
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +6 -6
- edsl/agents/Invigilator.py +3 -6
- edsl/agents/InvigilatorBase.py +27 -8
- edsl/agents/{PromptConstructor.py → PromptConstructionMixin.py} +29 -101
- edsl/config.py +34 -26
- edsl/coop/coop.py +2 -11
- edsl/data_transfer_models.py +73 -26
- edsl/enums.py +0 -2
- edsl/inference_services/GoogleService.py +1 -1
- edsl/inference_services/InferenceServiceABC.py +13 -44
- edsl/inference_services/OpenAIService.py +4 -7
- edsl/inference_services/TestService.py +15 -24
- edsl/inference_services/registry.py +0 -2
- edsl/jobs/Jobs.py +8 -18
- edsl/jobs/buckets/BucketCollection.py +15 -24
- edsl/jobs/buckets/TokenBucket.py +10 -64
- edsl/jobs/interviews/Interview.py +47 -115
- edsl/jobs/interviews/InterviewExceptionEntry.py +0 -2
- edsl/jobs/interviews/{InterviewExceptionCollection.py → interview_exception_tracking.py} +0 -16
- edsl/jobs/interviews/retry_management.py +39 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +170 -95
- edsl/jobs/runners/JobsRunnerStatusMixin.py +333 -0
- edsl/jobs/tasks/TaskHistory.py +0 -17
- edsl/language_models/LanguageModel.py +31 -26
- edsl/language_models/registry.py +9 -13
- edsl/questions/QuestionBase.py +14 -63
- edsl/questions/QuestionBudget.py +41 -93
- edsl/questions/QuestionFreeText.py +0 -6
- edsl/questions/QuestionMultipleChoice.py +23 -8
- edsl/questions/QuestionNumerical.py +4 -5
- edsl/questions/ResponseValidatorABC.py +5 -6
- edsl/questions/derived/QuestionLinearScale.py +1 -4
- edsl/questions/derived/QuestionTopK.py +1 -4
- edsl/questions/derived/QuestionYesNo.py +2 -8
- edsl/results/DatasetExportMixin.py +1 -5
- edsl/results/Result.py +1 -1
- edsl/results/Results.py +1 -4
- edsl/scenarios/FileStore.py +10 -71
- edsl/scenarios/Scenario.py +21 -86
- edsl/scenarios/ScenarioImageMixin.py +2 -2
- edsl/scenarios/ScenarioList.py +0 -13
- edsl/scenarios/ScenarioListPdfMixin.py +4 -150
- edsl/study/Study.py +0 -32
- edsl/surveys/Rule.py +1 -10
- edsl/surveys/RuleCollection.py +3 -19
- edsl/surveys/Survey.py +0 -7
- edsl/templates/error_reporting/interview_details.html +1 -6
- edsl/utilities/utilities.py +1 -9
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev2.dist-info}/METADATA +1 -2
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev2.dist-info}/RECORD +55 -61
- edsl/inference_services/TogetherAIService.py +0 -170
- edsl/jobs/runners/JobsRunnerStatus.py +0 -331
- edsl/questions/Quick.py +0 -41
- edsl/questions/templates/budget/__init__.py +0 -0
- edsl/questions/templates/budget/answering_instructions.jinja +0 -7
- edsl/questions/templates/budget/question_presentation.jinja +0 -7
- edsl/questions/templates/extract/__init__.py +0 -0
- edsl/questions/templates/rank/__init__.py +0 -0
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.33.dist-info → edsl-0.1.33.dev2.dist-info}/WHEEL +0 -0
edsl/Base.py
CHANGED
@@ -47,27 +47,21 @@ class PersistenceMixin:
|
|
47
47
|
self,
|
48
48
|
description: Optional[str] = None,
|
49
49
|
visibility: Optional[str] = "unlisted",
|
50
|
-
expected_parrot_url: Optional[str] = None,
|
51
50
|
):
|
52
51
|
"""Post the object to coop."""
|
53
52
|
from edsl.coop import Coop
|
54
53
|
|
55
|
-
c = Coop(
|
54
|
+
c = Coop()
|
56
55
|
return c.create(self, description, visibility)
|
57
56
|
|
58
57
|
@classmethod
|
59
|
-
def pull(
|
60
|
-
cls,
|
61
|
-
uuid: Optional[Union[str, UUID]] = None,
|
62
|
-
url: Optional[str] = None,
|
63
|
-
expected_parrot_url: Optional[str] = None,
|
64
|
-
):
|
58
|
+
def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
|
65
59
|
"""Pull the object from coop."""
|
66
60
|
from edsl.coop import Coop
|
67
61
|
from edsl.coop.utils import ObjectRegistry
|
68
62
|
|
69
63
|
object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
|
70
|
-
coop = Coop(
|
64
|
+
coop = Coop()
|
71
65
|
return coop.get(uuid, url, object_type)
|
72
66
|
|
73
67
|
@classmethod
|
edsl/__init__.py
CHANGED
@@ -23,7 +23,6 @@ from edsl.questions import QuestionNumerical
|
|
23
23
|
from edsl.questions import QuestionYesNo
|
24
24
|
from edsl.questions import QuestionBudget
|
25
25
|
from edsl.questions import QuestionRank
|
26
|
-
from edsl.questions import QuestionTopK
|
27
26
|
|
28
27
|
from edsl.scenarios import Scenario
|
29
28
|
from edsl.scenarios import ScenarioList
|
edsl/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.33"
|
1
|
+
__version__ = "0.1.33.dev1"
|
edsl/agents/Agent.py
CHANGED
@@ -586,9 +586,9 @@ class Agent(Base):
|
|
586
586
|
if dynamic_traits_func:
|
587
587
|
func = inspect.getsource(dynamic_traits_func)
|
588
588
|
raw_data["dynamic_traits_function_source_code"] = func
|
589
|
-
raw_data[
|
590
|
-
|
591
|
-
|
589
|
+
raw_data["dynamic_traits_function_name"] = (
|
590
|
+
self.dynamic_traits_function_name
|
591
|
+
)
|
592
592
|
if hasattr(self, "answer_question_directly"):
|
593
593
|
raw_data.pop(
|
594
594
|
"answer_question_directly", None
|
@@ -604,9 +604,9 @@ class Agent(Base):
|
|
604
604
|
raw_data["answer_question_directly_source_code"] = inspect.getsource(
|
605
605
|
answer_question_directly_func
|
606
606
|
)
|
607
|
-
raw_data[
|
608
|
-
|
609
|
-
|
607
|
+
raw_data["answer_question_directly_function_name"] = (
|
608
|
+
self.answer_question_directly_function_name
|
609
|
+
)
|
610
610
|
|
611
611
|
return raw_data
|
612
612
|
|
edsl/agents/Invigilator.py
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
from typing import Dict, Any, Optional
|
4
4
|
|
5
|
+
from edsl.exceptions import AgentRespondedWithBadJSONError
|
5
6
|
from edsl.prompts.Prompt import Prompt
|
6
7
|
from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
|
7
8
|
from edsl.prompts.registry import get_classes as prompt_lookup
|
8
9
|
from edsl.exceptions.questions import QuestionAnswerValidationError
|
10
|
+
from edsl.agents.PromptConstructionMixin import PromptConstructorMixin
|
9
11
|
from edsl.agents.InvigilatorBase import InvigilatorBase
|
10
12
|
from edsl.data_transfer_models import AgentResponseDict, EDSLResultObjectInput
|
11
|
-
from edsl.agents.PromptConstructor import PromptConstructor
|
12
13
|
|
13
14
|
|
14
15
|
class NotApplicable(str):
|
@@ -18,13 +19,9 @@ class NotApplicable(str):
|
|
18
19
|
return instance
|
19
20
|
|
20
21
|
|
21
|
-
class InvigilatorAI(InvigilatorBase):
|
22
|
+
class InvigilatorAI(PromptConstructorMixin, InvigilatorBase):
|
22
23
|
"""An invigilator that uses an AI model to answer questions."""
|
23
24
|
|
24
|
-
def get_prompts(self) -> Dict[str, Prompt]:
|
25
|
-
"""Return the prompts used."""
|
26
|
-
return self.prompt_constructor.get_prompts()
|
27
|
-
|
28
25
|
async def async_answer_question(self) -> AgentResponseDict:
|
29
26
|
"""Answer a question using the AI model.
|
30
27
|
|
edsl/agents/InvigilatorBase.py
CHANGED
@@ -14,7 +14,6 @@ from edsl.surveys.MemoryPlan import MemoryPlan
|
|
14
14
|
from edsl.language_models.LanguageModel import LanguageModel
|
15
15
|
|
16
16
|
from edsl.data_transfer_models import EDSLResultObjectInput
|
17
|
-
from edsl.agents.PromptConstructor import PromptConstructor
|
18
17
|
|
19
18
|
|
20
19
|
class InvigilatorBase(ABC):
|
@@ -28,7 +27,16 @@ class InvigilatorBase(ABC):
|
|
28
27
|
|
29
28
|
This returns an empty prompt because there is no memory the agent needs to have at q0.
|
30
29
|
|
30
|
+
>>> InvigilatorBase.example().create_memory_prompt("q0")
|
31
|
+
Prompt(text=\"""\""")
|
31
32
|
|
33
|
+
>>> i = InvigilatorBase.example()
|
34
|
+
>>> i.current_answers = {"q0": "Prior answer"}
|
35
|
+
>>> i.memory_plan.add_single_memory("q1", "q0")
|
36
|
+
>>> i.create_memory_prompt("q1")
|
37
|
+
Prompt(text=\"""
|
38
|
+
Before the question you are now answering, you already answered the following question(s):
|
39
|
+
...
|
32
40
|
"""
|
33
41
|
|
34
42
|
def __init__(
|
@@ -64,11 +72,6 @@ class InvigilatorBase(ABC):
|
|
64
72
|
None # placeholder for the raw response from the model
|
65
73
|
)
|
66
74
|
|
67
|
-
@property
|
68
|
-
def prompt_constructor(self) -> PromptConstructor:
|
69
|
-
"""Return the prompt constructor."""
|
70
|
-
return PromptConstructor(self)
|
71
|
-
|
72
75
|
def to_dict(self):
|
73
76
|
attributes = [
|
74
77
|
"agent",
|
@@ -204,6 +207,22 @@ class InvigilatorBase(ABC):
|
|
204
207
|
|
205
208
|
return main()
|
206
209
|
|
210
|
+
def create_memory_prompt(self, question_name: str) -> Prompt:
|
211
|
+
"""Create a memory for the agent.
|
212
|
+
|
213
|
+
The returns a memory prompt for the agent.
|
214
|
+
|
215
|
+
>>> i = InvigilatorBase.example()
|
216
|
+
>>> i.current_answers = {"q0": "Prior answer"}
|
217
|
+
>>> i.memory_plan.add_single_memory("q1", "q0")
|
218
|
+
>>> p = i.create_memory_prompt("q1")
|
219
|
+
>>> p.text.strip().replace("\\n", " ").replace("\\t", " ")
|
220
|
+
'Before the question you are now answering, you already answered the following question(s): Question: Do you like school? Answer: Prior answer'
|
221
|
+
"""
|
222
|
+
return self.memory_plan.get_memory_prompt_fragment(
|
223
|
+
question_name, self.current_answers
|
224
|
+
)
|
225
|
+
|
207
226
|
@classmethod
|
208
227
|
def example(
|
209
228
|
cls, throw_an_exception=False, question=None, scenario=None, survey=None
|
@@ -266,9 +285,9 @@ class InvigilatorBase(ABC):
|
|
266
285
|
|
267
286
|
memory_plan = MemoryPlan(survey=survey)
|
268
287
|
current_answers = None
|
269
|
-
from edsl.agents.
|
288
|
+
from edsl.agents.PromptConstructionMixin import PromptConstructorMixin
|
270
289
|
|
271
|
-
class InvigilatorExample(InvigilatorBase):
|
290
|
+
class InvigilatorExample(PromptConstructorMixin, InvigilatorBase):
|
272
291
|
"""An example invigilator."""
|
273
292
|
|
274
293
|
async def async_answer_question(self):
|
@@ -1,15 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Dict, Any, Optional
|
2
|
+
from typing import Dict, Any, Optional
|
3
3
|
from collections import UserList
|
4
|
-
import enum
|
5
|
-
|
6
|
-
from jinja2 import Environment, meta
|
7
4
|
|
5
|
+
# from functools import reduce
|
8
6
|
from edsl.prompts.Prompt import Prompt
|
9
|
-
|
7
|
+
|
8
|
+
# from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
|
10
9
|
from edsl.prompts.registry import get_classes as prompt_lookup
|
11
10
|
from edsl.exceptions import QuestionScenarioRenderError
|
12
11
|
|
12
|
+
import enum
|
13
|
+
|
13
14
|
|
14
15
|
class PromptComponent(enum.Enum):
|
15
16
|
AGENT_INSTRUCTIONS = "agent_instructions"
|
@@ -18,21 +19,6 @@ class PromptComponent(enum.Enum):
|
|
18
19
|
PRIOR_QUESTION_MEMORY = "prior_question_memory"
|
19
20
|
|
20
21
|
|
21
|
-
def get_jinja2_variables(template_str: str) -> Set[str]:
|
22
|
-
"""
|
23
|
-
Extracts all variable names from a Jinja2 template using Jinja2's built-in parsing.
|
24
|
-
|
25
|
-
Args:
|
26
|
-
template_str (str): The Jinja2 template string
|
27
|
-
|
28
|
-
Returns:
|
29
|
-
Set[str]: A set of variable names found in the template
|
30
|
-
"""
|
31
|
-
env = Environment()
|
32
|
-
ast = env.parse(template_str)
|
33
|
-
return meta.find_undeclared_variables(ast)
|
34
|
-
|
35
|
-
|
36
22
|
class PromptList(UserList):
|
37
23
|
separator = Prompt(" ")
|
38
24
|
|
@@ -151,7 +137,7 @@ class PromptPlan:
|
|
151
137
|
}
|
152
138
|
|
153
139
|
|
154
|
-
class
|
140
|
+
class PromptConstructorMixin:
|
155
141
|
"""Mixin for constructing prompts for the LLM call.
|
156
142
|
|
157
143
|
The pieces of a prompt are:
|
@@ -163,40 +149,16 @@ class PromptConstructor:
|
|
163
149
|
This is mixed into the Invigilator class.
|
164
150
|
"""
|
165
151
|
|
166
|
-
|
167
|
-
self.invigilator = invigilator
|
168
|
-
self.agent = invigilator.agent
|
169
|
-
self.question = invigilator.question
|
170
|
-
self.scenario = invigilator.scenario
|
171
|
-
self.survey = invigilator.survey
|
172
|
-
self.model = invigilator.model
|
173
|
-
self.current_answers = invigilator.current_answers
|
174
|
-
self.memory_plan = invigilator.memory_plan
|
175
|
-
self.prompt_plan = PromptPlan() # Assuming PromptPlan is defined elsewhere
|
176
|
-
|
177
|
-
# prompt_plan = PromptPlan()
|
178
|
-
|
179
|
-
@property
|
180
|
-
def scenario_image_keys(self):
|
181
|
-
image_entries = []
|
182
|
-
|
183
|
-
for key, value in self.scenario.items():
|
184
|
-
if isinstance(value, ImageInfo):
|
185
|
-
image_entries.append(key)
|
186
|
-
return image_entries
|
152
|
+
prompt_plan = PromptPlan()
|
187
153
|
|
188
154
|
@property
|
189
155
|
def agent_instructions_prompt(self) -> Prompt:
|
190
156
|
"""
|
191
157
|
>>> from edsl.agents.InvigilatorBase import InvigilatorBase
|
192
158
|
>>> i = InvigilatorBase.example()
|
193
|
-
>>> i.
|
159
|
+
>>> i.agent_instructions_prompt
|
194
160
|
Prompt(text=\"""You are answering questions as if you were a human. Do not break character.\""")
|
195
161
|
"""
|
196
|
-
from edsl import Agent
|
197
|
-
|
198
|
-
if self.agent == Agent(): # if agent is empty, then return an empty prompt
|
199
|
-
return Prompt(text="")
|
200
162
|
if not hasattr(self, "_agent_instructions_prompt"):
|
201
163
|
applicable_prompts = prompt_lookup(
|
202
164
|
component_type="agent_instructions",
|
@@ -214,17 +176,12 @@ class PromptConstructor:
|
|
214
176
|
"""
|
215
177
|
>>> from edsl.agents.InvigilatorBase import InvigilatorBase
|
216
178
|
>>> i = InvigilatorBase.example()
|
217
|
-
>>> i.
|
179
|
+
>>> i.agent_persona_prompt
|
218
180
|
Prompt(text=\"""You are an agent with the following persona:
|
219
181
|
{'age': 22, 'hair': 'brown', 'height': 5.5}\""")
|
220
182
|
|
221
183
|
"""
|
222
184
|
if not hasattr(self, "_agent_persona_prompt"):
|
223
|
-
from edsl import Agent
|
224
|
-
|
225
|
-
if self.agent == Agent(): # if agent is empty, then return an empty prompt
|
226
|
-
return Prompt(text="")
|
227
|
-
|
228
185
|
if not hasattr(self.agent, "agent_persona"):
|
229
186
|
applicable_prompts = prompt_lookup(
|
230
187
|
component_type="agent_persona",
|
@@ -269,29 +226,27 @@ class PromptConstructor:
|
|
269
226
|
d[new_question].comment = answer
|
270
227
|
return d
|
271
228
|
|
272
|
-
@property
|
273
|
-
def question_image_keys(self):
|
274
|
-
raw_question_text = self.question.question_text
|
275
|
-
variables = get_jinja2_variables(raw_question_text)
|
276
|
-
question_image_keys = []
|
277
|
-
for var in variables:
|
278
|
-
if var in self.scenario_image_keys:
|
279
|
-
question_image_keys.append(var)
|
280
|
-
return question_image_keys
|
281
|
-
|
282
229
|
@property
|
283
230
|
def question_instructions_prompt(self) -> Prompt:
|
284
231
|
"""
|
285
232
|
>>> from edsl.agents.InvigilatorBase import InvigilatorBase
|
286
233
|
>>> i = InvigilatorBase.example()
|
287
|
-
>>> i.
|
234
|
+
>>> i.question_instructions_prompt
|
288
235
|
Prompt(text=\"""...
|
289
236
|
...
|
290
237
|
"""
|
291
238
|
if not hasattr(self, "_question_instructions_prompt"):
|
292
239
|
question_prompt = self.question.get_instructions(model=self.model.model)
|
293
240
|
|
294
|
-
#
|
241
|
+
# TODO: Try to populate the answers in the question object if they are available
|
242
|
+
# d = self.survey.question_names_to_questions()
|
243
|
+
# for question, answer in self.current_answers.items():
|
244
|
+
# if question in d:
|
245
|
+
# d[question].answer = answer
|
246
|
+
# else:
|
247
|
+
# # adds a comment to the question
|
248
|
+
# if (new_question := question.split("_comment")[0]) in d:
|
249
|
+
# d[new_question].comment = answer
|
295
250
|
|
296
251
|
question_data = self.question.data.copy()
|
297
252
|
|
@@ -299,6 +254,8 @@ class PromptConstructor:
|
|
299
254
|
# This is used when the user is using the question_options as a variable from a sceario
|
300
255
|
# if "question_options" in question_data:
|
301
256
|
if isinstance(self.question.data.get("question_options", None), str):
|
257
|
+
from jinja2 import Environment, meta
|
258
|
+
|
302
259
|
env = Environment()
|
303
260
|
parsed_content = env.parse(self.question.data["question_options"])
|
304
261
|
question_option_key = list(
|
@@ -312,13 +269,8 @@ class PromptConstructor:
|
|
312
269
|
self.question.question_options = question_options
|
313
270
|
|
314
271
|
replacement_dict = (
|
315
|
-
|
316
|
-
|
|
317
|
-
| {
|
318
|
-
k: v
|
319
|
-
for k, v in self.scenario.items()
|
320
|
-
if k not in self.scenario_image_keys
|
321
|
-
} # don't include images in the replacement dict
|
272
|
+
question_data
|
273
|
+
| self.scenario
|
322
274
|
| self.prior_answers_dict()
|
323
275
|
| {"agent": self.agent}
|
324
276
|
| {
|
@@ -328,10 +280,9 @@ class PromptConstructor:
|
|
328
280
|
),
|
329
281
|
}
|
330
282
|
)
|
331
|
-
|
283
|
+
# breakpoint()
|
332
284
|
rendered_instructions = question_prompt.render(replacement_dict)
|
333
|
-
|
334
|
-
# is there anything left to render?
|
285
|
+
# breakpoint()
|
335
286
|
undefined_template_variables = (
|
336
287
|
rendered_instructions.undefined_template_variables({})
|
337
288
|
)
|
@@ -349,9 +300,7 @@ class PromptConstructor:
|
|
349
300
|
f"Question instructions still has variables: {undefined_template_variables}."
|
350
301
|
)
|
351
302
|
|
352
|
-
|
353
|
-
# Check if question has instructions - these are instructions in a Survey that can apply to multiple follow-on questions
|
354
|
-
####################################
|
303
|
+
# Check if question has an instructions
|
355
304
|
relevant_instructions = self.survey.relevant_instructions(
|
356
305
|
self.question.question_name
|
357
306
|
)
|
@@ -380,23 +329,6 @@ class PromptConstructor:
|
|
380
329
|
self._prior_question_memory_prompt = memory_prompt
|
381
330
|
return self._prior_question_memory_prompt
|
382
331
|
|
383
|
-
def create_memory_prompt(self, question_name: str) -> Prompt:
|
384
|
-
"""Create a memory for the agent.
|
385
|
-
|
386
|
-
The returns a memory prompt for the agent.
|
387
|
-
|
388
|
-
>>> from edsl.agents.InvigilatorBase import InvigilatorBase
|
389
|
-
>>> i = InvigilatorBase.example()
|
390
|
-
>>> i.current_answers = {"q0": "Prior answer"}
|
391
|
-
>>> i.memory_plan.add_single_memory("q1", "q0")
|
392
|
-
>>> p = i.prompt_constructor.create_memory_prompt("q1")
|
393
|
-
>>> p.text.strip().replace("\\n", " ").replace("\\t", " ")
|
394
|
-
'Before the question you are now answering, you already answered the following question(s): Question: Do you like school? Answer: Prior answer'
|
395
|
-
"""
|
396
|
-
return self.memory_plan.get_memory_prompt_fragment(
|
397
|
-
question_name, self.current_answers
|
398
|
-
)
|
399
|
-
|
400
332
|
def construct_system_prompt(self) -> Prompt:
|
401
333
|
"""Construct the system prompt for the LLM call."""
|
402
334
|
import warnings
|
@@ -431,13 +363,9 @@ class PromptConstructor:
|
|
431
363
|
question_instructions=self.question_instructions_prompt,
|
432
364
|
prior_question_memory=self.prior_question_memory_prompt,
|
433
365
|
)
|
434
|
-
if len(self.question_image_keys) > 1:
|
435
|
-
raise ValueError("We can only handle one image per question.")
|
436
|
-
elif len(self.question_image_keys) == 1:
|
437
|
-
prompts["encoded_image"] = self.scenario[
|
438
|
-
self.question_image_keys[0]
|
439
|
-
].encoded_image
|
440
366
|
|
367
|
+
if hasattr(self.scenario, "has_image") and self.scenario.has_image:
|
368
|
+
prompts["encoded_image"] = self.scenario["encoded_image"]
|
441
369
|
return prompts
|
442
370
|
|
443
371
|
def _get_scenario_with_image(self) -> Scenario:
|
edsl/config.py
CHANGED
@@ -1,65 +1,73 @@
|
|
1
1
|
"""This module provides a Config class that loads environment variables from a .env file and sets them as class attributes."""
|
2
2
|
|
3
3
|
import os
|
4
|
-
from dotenv import load_dotenv, find_dotenv
|
5
4
|
from edsl.exceptions import (
|
6
5
|
InvalidEnvironmentVariableError,
|
7
6
|
MissingEnvironmentVariableError,
|
8
7
|
)
|
8
|
+
from dotenv import load_dotenv, find_dotenv
|
9
9
|
|
10
10
|
# valid values for EDSL_RUN_MODE
|
11
|
-
EDSL_RUN_MODES = [
|
12
|
-
"development",
|
13
|
-
"development-testrun",
|
14
|
-
"production",
|
15
|
-
]
|
11
|
+
EDSL_RUN_MODES = ["development", "development-testrun", "production"]
|
16
12
|
|
17
13
|
# `default` is used to impute values only in "production" mode
|
18
14
|
# `info` gives a brief description of the env var
|
19
15
|
CONFIG_MAP = {
|
20
16
|
"EDSL_RUN_MODE": {
|
21
17
|
"default": "production",
|
22
|
-
"info": "This
|
18
|
+
"info": "This env var determines the run mode of the application.",
|
19
|
+
},
|
20
|
+
"EDSL_DATABASE_PATH": {
|
21
|
+
"default": f"sqlite:///{os.path.join(os.getcwd(), '.edsl_cache/data.db')}",
|
22
|
+
"info": "This env var determines the path to the cache file.",
|
23
|
+
},
|
24
|
+
"EDSL_LOGGING_PATH": {
|
25
|
+
"default": f"{os.path.join(os.getcwd(), 'interview.log')}",
|
26
|
+
"info": "This env var determines the path to the log file.",
|
23
27
|
},
|
24
28
|
"EDSL_API_TIMEOUT": {
|
25
29
|
"default": "60",
|
26
|
-
"info": "This
|
30
|
+
"info": "This env var determines the maximum number of seconds to wait for an API call to return.",
|
27
31
|
},
|
28
32
|
"EDSL_BACKOFF_START_SEC": {
|
29
33
|
"default": "1",
|
30
|
-
"info": "This
|
34
|
+
"info": "This env var determines the number of seconds to wait before retrying a failed API call.",
|
31
35
|
},
|
32
|
-
"
|
36
|
+
"EDSL_MAX_BACKOFF_SEC": {
|
33
37
|
"default": "60",
|
34
|
-
"info": "This
|
38
|
+
"info": "This env var determines the maximum number of seconds to wait before retrying a failed API call.",
|
35
39
|
},
|
36
|
-
"
|
37
|
-
"default":
|
38
|
-
"info": "This
|
40
|
+
"EDSL_MAX_ATTEMPTS": {
|
41
|
+
"default": "5",
|
42
|
+
"info": "This env var determines the maximum number of times to retry a failed API call.",
|
39
43
|
},
|
40
44
|
"EDSL_DEFAULT_MODEL": {
|
41
45
|
"default": "gpt-4o",
|
42
|
-
"info": "This
|
46
|
+
"info": "This env var holds the default model name.",
|
43
47
|
},
|
44
|
-
"
|
45
|
-
"default": "
|
46
|
-
"info": "This
|
47
|
-
},
|
48
|
-
"EDSL_MAX_ATTEMPTS": {
|
49
|
-
"default": "5",
|
50
|
-
"info": "This config var determines the maximum number of times to retry a failed API call.",
|
48
|
+
"EDSL_SERVICE_TPM_BASELINE": {
|
49
|
+
"default": "2000000",
|
50
|
+
"info": "This env var holds the maximum number of tokens per minute for all models. Model-specific values such as EDSL_SERVICE_TPM_OPENAI will override this.",
|
51
51
|
},
|
52
52
|
"EDSL_SERVICE_RPM_BASELINE": {
|
53
53
|
"default": "100",
|
54
|
-
"info": "This
|
54
|
+
"info": "This env var holds the maximum number of requests per minute for OpenAI. Model-specific values such as EDSL_SERVICE_RPM_OPENAI will override this.",
|
55
55
|
},
|
56
|
-
"
|
56
|
+
"EDSL_SERVICE_TPM_OPENAI": {
|
57
57
|
"default": "2000000",
|
58
|
-
"info": "This
|
58
|
+
"info": "This env var holds the maximum number of tokens per minute for OpenAI.",
|
59
|
+
},
|
60
|
+
"EDSL_SERVICE_RPM_OPENAI": {
|
61
|
+
"default": "100",
|
62
|
+
"info": "This env var holds the maximum number of requests per minute for OpenAI.",
|
63
|
+
},
|
64
|
+
"EDSL_FETCH_TOKEN_PRICES": {
|
65
|
+
"default": "True",
|
66
|
+
"info": "Whether to fetch the prices for tokens",
|
59
67
|
},
|
60
68
|
"EXPECTED_PARROT_URL": {
|
61
69
|
"default": "https://www.expectedparrot.com",
|
62
|
-
"info": "This
|
70
|
+
"info": "This env var holds the URL of the Expected Parrot API.",
|
63
71
|
},
|
64
72
|
}
|
65
73
|
|
edsl/coop/coop.py
CHANGED
@@ -59,16 +59,8 @@ class Coop:
|
|
59
59
|
Send a request to the server and return the response.
|
60
60
|
"""
|
61
61
|
url = f"{self.url}/{uri}"
|
62
|
-
method = method.upper()
|
63
|
-
if payload is None:
|
64
|
-
timeout = 20
|
65
|
-
elif (
|
66
|
-
method.upper() == "POST"
|
67
|
-
and "json_string" in payload
|
68
|
-
and payload.get("json_string") is not None
|
69
|
-
):
|
70
|
-
timeout = max(20, (len(payload.get("json_string", "")) // (1024 * 1024)))
|
71
62
|
try:
|
63
|
+
method = method.upper()
|
72
64
|
if method in ["GET", "DELETE"]:
|
73
65
|
response = requests.request(
|
74
66
|
method, url, params=params, headers=self.headers, timeout=timeout
|
@@ -85,7 +77,7 @@ class Coop:
|
|
85
77
|
else:
|
86
78
|
raise Exception(f"Invalid {method=}.")
|
87
79
|
except requests.ConnectionError:
|
88
|
-
raise requests.ConnectionError(
|
80
|
+
raise requests.ConnectionError("Could not connect to the server.")
|
89
81
|
|
90
82
|
return response
|
91
83
|
|
@@ -95,7 +87,6 @@ class Coop:
|
|
95
87
|
"""
|
96
88
|
if response.status_code >= 400:
|
97
89
|
message = response.json().get("detail")
|
98
|
-
# print(response.text)
|
99
90
|
if "Authorization" in message:
|
100
91
|
print(message)
|
101
92
|
message = "Please provide an Expected Parrot API key."
|
edsl/data_transfer_models.py
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
from typing import NamedTuple, Dict, List, Optional, Any
|
2
|
-
from dataclasses import dataclass, fields
|
3
|
-
import reprlib
|
4
2
|
|
5
3
|
|
6
4
|
class ModelInputs(NamedTuple):
|
@@ -47,27 +45,76 @@ class EDSLResultObjectInput(NamedTuple):
|
|
47
45
|
cost: Optional[float] = None
|
48
46
|
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
48
|
+
# from collections import UserDict
|
49
|
+
|
50
|
+
|
51
|
+
# class AgentResponseDict(UserDict):
|
52
|
+
# """A dictionary to store the response of the agent to a question."""
|
53
|
+
|
54
|
+
# def __init__(
|
55
|
+
# self,
|
56
|
+
# *,
|
57
|
+
# question_name,
|
58
|
+
# answer,
|
59
|
+
# prompts,
|
60
|
+
# generated_tokens: str,
|
61
|
+
# usage=None,
|
62
|
+
# comment=None,
|
63
|
+
# cached_response=None,
|
64
|
+
# raw_model_response=None,
|
65
|
+
# simple_model_raw_response=None,
|
66
|
+
# cache_used=None,
|
67
|
+
# cache_key=None,
|
68
|
+
# ):
|
69
|
+
# """Initialize the AgentResponseDict object."""
|
70
|
+
# usage = usage or {"prompt_tokens": 0, "completion_tokens": 0}
|
71
|
+
# if generated_tokens is None:
|
72
|
+
# raise ValueError("generated_tokens must be provided")
|
73
|
+
# self.data = {
|
74
|
+
# "answer": answer,
|
75
|
+
# "comment": comment,
|
76
|
+
# "question_name": question_name,
|
77
|
+
# "prompts": prompts,
|
78
|
+
# "usage": usage,
|
79
|
+
# "cached_response": cached_response,
|
80
|
+
# "raw_model_response": raw_model_response,
|
81
|
+
# "simple_model_raw_response": simple_model_raw_response,
|
82
|
+
# "cache_used": cache_used,
|
83
|
+
# "cache_key": cache_key,
|
84
|
+
# "generated_tokens": generated_tokens,
|
85
|
+
# }
|
86
|
+
|
87
|
+
# @property
|
88
|
+
# def data(self):
|
89
|
+
# return self._data
|
90
|
+
|
91
|
+
# @data.setter
|
92
|
+
# def data(self, value):
|
93
|
+
# self._data = value
|
94
|
+
|
95
|
+
# def __getitem__(self, key):
|
96
|
+
# return self.data.get(key, None)
|
97
|
+
|
98
|
+
# def __setitem__(self, key, value):
|
99
|
+
# self.data[key] = value
|
100
|
+
|
101
|
+
# def __delitem__(self, key):
|
102
|
+
# del self.data[key]
|
103
|
+
|
104
|
+
# def __iter__(self):
|
105
|
+
# return iter(self.data)
|
106
|
+
|
107
|
+
# def __len__(self):
|
108
|
+
# return len(self.data)
|
109
|
+
|
110
|
+
# def keys(self):
|
111
|
+
# return self.data.keys()
|
112
|
+
|
113
|
+
# def values(self):
|
114
|
+
# return self.data.values()
|
115
|
+
|
116
|
+
# def items(self):
|
117
|
+
# return self.data.items()
|
118
|
+
|
119
|
+
# def is_this_same_model(self):
|
120
|
+
# return True
|
edsl/enums.py
CHANGED
@@ -63,7 +63,6 @@ class InferenceServiceType(EnumWithChecks):
|
|
63
63
|
AZURE = "azure"
|
64
64
|
OLLAMA = "ollama"
|
65
65
|
MISTRAL = "mistral"
|
66
|
-
TOGETHER = "together"
|
67
66
|
|
68
67
|
|
69
68
|
service_to_api_keyname = {
|
@@ -77,7 +76,6 @@ service_to_api_keyname = {
|
|
77
76
|
InferenceServiceType.GROQ.value: "GROQ_API_KEY",
|
78
77
|
InferenceServiceType.BEDROCK.value: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
|
79
78
|
InferenceServiceType.MISTRAL.value: "MISTRAL_API_KEY",
|
80
|
-
InferenceServiceType.TOGETHER.value: "TOGETHER_API_KEY",
|
81
79
|
}
|
82
80
|
|
83
81
|
|