edsl 0.1.31__py3-none-any.whl → 0.1.31.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/__version__.py +1 -1
- edsl/agents/Invigilator.py +2 -7
- edsl/agents/PromptConstructionMixin.py +4 -9
- edsl/config.py +0 -4
- edsl/conjure/Conjure.py +0 -6
- edsl/coop/coop.py +0 -4
- edsl/data/CacheHandler.py +4 -3
- edsl/enums.py +0 -2
- edsl/inference_services/DeepInfraService.py +91 -6
- edsl/inference_services/InferenceServicesCollection.py +8 -13
- edsl/inference_services/OpenAIService.py +21 -64
- edsl/inference_services/registry.py +1 -2
- edsl/jobs/Jobs.py +5 -29
- edsl/jobs/buckets/TokenBucket.py +4 -12
- edsl/jobs/interviews/Interview.py +9 -31
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +33 -49
- edsl/jobs/interviews/interview_exception_tracking.py +10 -68
- edsl/jobs/runners/JobsRunnerAsyncio.py +81 -112
- edsl/jobs/runners/JobsRunnerStatusData.py +237 -0
- edsl/jobs/runners/JobsRunnerStatusMixin.py +35 -291
- edsl/jobs/tasks/TaskCreators.py +2 -8
- edsl/jobs/tasks/TaskHistory.py +1 -145
- edsl/language_models/LanguageModel.py +32 -49
- edsl/language_models/registry.py +0 -4
- edsl/questions/QuestionMultipleChoice.py +1 -1
- edsl/questions/QuestionNumerical.py +1 -0
- edsl/results/DatasetExportMixin.py +3 -12
- edsl/scenarios/Scenario.py +0 -14
- edsl/scenarios/ScenarioList.py +2 -15
- edsl/scenarios/ScenarioListExportMixin.py +4 -15
- edsl/scenarios/ScenarioListPdfMixin.py +0 -3
- {edsl-0.1.31.dist-info → edsl-0.1.31.dev2.dist-info}/METADATA +1 -2
- {edsl-0.1.31.dist-info → edsl-0.1.31.dev2.dist-info}/RECORD +35 -37
- edsl/inference_services/GroqService.py +0 -18
- edsl/jobs/interviews/InterviewExceptionEntry.py +0 -101
- {edsl-0.1.31.dist-info → edsl-0.1.31.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.31.dist-info → edsl-0.1.31.dev2.dist-info}/WHEEL +0 -0
edsl/jobs/tasks/TaskHistory.py
CHANGED
@@ -11,8 +11,6 @@ class TaskHistory:
|
|
11
11
|
|
12
12
|
[Interview.exceptions, Interview.exceptions, Interview.exceptions, ...]
|
13
13
|
|
14
|
-
>>> _ = TaskHistory.example()
|
15
|
-
...
|
16
14
|
"""
|
17
15
|
|
18
16
|
self.total_interviews = interviews
|
@@ -20,26 +18,8 @@ class TaskHistory:
|
|
20
18
|
|
21
19
|
self._interviews = {index: i for index, i in enumerate(self.total_interviews)}
|
22
20
|
|
23
|
-
@classmethod
|
24
|
-
def example(cls):
|
25
|
-
from edsl.jobs.interviews.Interview import Interview
|
26
|
-
|
27
|
-
from edsl.jobs.Jobs import Jobs
|
28
|
-
|
29
|
-
j = Jobs.example(throw_exception_probability=1, test_model=True)
|
30
|
-
|
31
|
-
from edsl.config import CONFIG
|
32
|
-
|
33
|
-
results = j.run(print_exceptions=False, skip_retry=True, cache = False)
|
34
|
-
|
35
|
-
return cls(results.task_history.total_interviews)
|
36
|
-
|
37
21
|
@property
|
38
22
|
def exceptions(self):
|
39
|
-
"""
|
40
|
-
>>> len(TaskHistory.example().exceptions)
|
41
|
-
4
|
42
|
-
"""
|
43
23
|
return [i.exceptions for k, i in self._interviews.items() if i.exceptions != {}]
|
44
24
|
|
45
25
|
@property
|
@@ -62,12 +42,7 @@ class TaskHistory:
|
|
62
42
|
|
63
43
|
@property
|
64
44
|
def has_exceptions(self) -> bool:
|
65
|
-
"""Return True if there are any exceptions.
|
66
|
-
|
67
|
-
>>> TaskHistory.example().has_exceptions
|
68
|
-
True
|
69
|
-
|
70
|
-
"""
|
45
|
+
"""Return True if there are any exceptions."""
|
71
46
|
return len(self.exceptions) > 0
|
72
47
|
|
73
48
|
def _repr_html_(self):
|
@@ -241,47 +216,6 @@ class TaskHistory:
|
|
241
216
|
}
|
242
217
|
"""
|
243
218
|
|
244
|
-
@property
|
245
|
-
def exceptions_by_type(self) -> dict:
|
246
|
-
"""Return a dictionary of exceptions by type."""
|
247
|
-
exceptions_by_type = {}
|
248
|
-
for interview in self.total_interviews:
|
249
|
-
for question_name, exceptions in interview.exceptions.items():
|
250
|
-
for exception in exceptions:
|
251
|
-
exception_type = exception["exception"]
|
252
|
-
if exception_type in exceptions_by_type:
|
253
|
-
exceptions_by_type[exception_type] += 1
|
254
|
-
else:
|
255
|
-
exceptions_by_type[exception_type] = 1
|
256
|
-
return exceptions_by_type
|
257
|
-
|
258
|
-
@property
|
259
|
-
def exceptions_by_question_name(self) -> dict:
|
260
|
-
"""Return a dictionary of exceptions tallied by question name."""
|
261
|
-
exceptions_by_question_name = {}
|
262
|
-
for interview in self.total_interviews:
|
263
|
-
for question_name, exceptions in interview.exceptions.items():
|
264
|
-
if question_name not in exceptions_by_question_name:
|
265
|
-
exceptions_by_question_name[question_name] = 0
|
266
|
-
exceptions_by_question_name[question_name] += len(exceptions)
|
267
|
-
|
268
|
-
for question in self.total_interviews[0].survey.questions:
|
269
|
-
if question.question_name not in exceptions_by_question_name:
|
270
|
-
exceptions_by_question_name[question.question_name] = 0
|
271
|
-
return exceptions_by_question_name
|
272
|
-
|
273
|
-
@property
|
274
|
-
def exceptions_by_model(self) -> dict:
|
275
|
-
"""Return a dictionary of exceptions tallied by model and question name."""
|
276
|
-
exceptions_by_model = {}
|
277
|
-
for interview in self.total_interviews:
|
278
|
-
model = interview.model
|
279
|
-
if model not in exceptions_by_model:
|
280
|
-
exceptions_by_model[model.model] = 0
|
281
|
-
if interview.exceptions != {}:
|
282
|
-
exceptions_by_model[model.model] += len(interview.exceptions)
|
283
|
-
return exceptions_by_model
|
284
|
-
|
285
219
|
def html(
|
286
220
|
self,
|
287
221
|
filename: Optional[str] = None,
|
@@ -302,8 +236,6 @@ class TaskHistory:
|
|
302
236
|
if css is None:
|
303
237
|
css = self.css()
|
304
238
|
|
305
|
-
models_used = set([i.model for index, i in self._interviews.items()])
|
306
|
-
|
307
239
|
template = Template(
|
308
240
|
"""
|
309
241
|
<!DOCTYPE html>
|
@@ -317,69 +249,6 @@ class TaskHistory:
|
|
317
249
|
</style>
|
318
250
|
</head>
|
319
251
|
<body>
|
320
|
-
<h1>Overview</h1>
|
321
|
-
<p>There were {{ interviews|length }} total interviews. The number of interviews with exceptions was {{ num_exceptions }}.</p>
|
322
|
-
<p>The models used were: {{ models_used }}.</p>
|
323
|
-
<p>For documentation on dealing with exceptions on Expected Parrot,
|
324
|
-
see <a href="https://docs.expectedparrot.com/en/latest/exceptions.html">here</a>.</p>
|
325
|
-
|
326
|
-
<h2>Exceptions by Type</h2>
|
327
|
-
<table>
|
328
|
-
<thead>
|
329
|
-
<tr>
|
330
|
-
<th>Exception Type</th>
|
331
|
-
<th>Number</th>
|
332
|
-
</tr>
|
333
|
-
</thead>
|
334
|
-
<tbody>
|
335
|
-
{% for exception_type, exceptions in exceptions_by_type.items() %}
|
336
|
-
<tr>
|
337
|
-
<td>{{ exception_type }}</td>
|
338
|
-
<td>{{ exceptions }}</td>
|
339
|
-
</tr>
|
340
|
-
{% endfor %}
|
341
|
-
</tbody>
|
342
|
-
</table>
|
343
|
-
|
344
|
-
|
345
|
-
<h2>Exceptions by Model</h2>
|
346
|
-
<table>
|
347
|
-
<thead>
|
348
|
-
<tr>
|
349
|
-
<th>Model</th>
|
350
|
-
<th>Number</th>
|
351
|
-
</tr>
|
352
|
-
</thead>
|
353
|
-
<tbody>
|
354
|
-
{% for model, exceptions in exceptions_by_model.items() %}
|
355
|
-
<tr>
|
356
|
-
<td>{{ model }}</td>
|
357
|
-
<td>{{ exceptions }}</td>
|
358
|
-
</tr>
|
359
|
-
{% endfor %}
|
360
|
-
</tbody>
|
361
|
-
</table>
|
362
|
-
|
363
|
-
|
364
|
-
<h2>Exceptions by Question Name</h2>
|
365
|
-
<table>
|
366
|
-
<thead>
|
367
|
-
<tr>
|
368
|
-
<th>Question Name</th>
|
369
|
-
<th>Number of Exceptions</th>
|
370
|
-
</tr>
|
371
|
-
</thead>
|
372
|
-
<tbody>
|
373
|
-
{% for question_name, exception_count in exceptions_by_question_name.items() %}
|
374
|
-
<tr>
|
375
|
-
<td>{{ question_name }}</td>
|
376
|
-
<td>{{ exception_count }}</td>
|
377
|
-
</tr>
|
378
|
-
{% endfor %}
|
379
|
-
</tbody>
|
380
|
-
</table>
|
381
|
-
|
382
|
-
|
383
252
|
{% for index, interview in interviews.items() %}
|
384
253
|
{% if interview.exceptions != {} %}
|
385
254
|
<div class="interview">Interview: {{ index }} </div>
|
@@ -427,18 +296,11 @@ class TaskHistory:
|
|
427
296
|
"""
|
428
297
|
)
|
429
298
|
|
430
|
-
# breakpoint()
|
431
|
-
|
432
299
|
# Render the template with data
|
433
300
|
output = template.render(
|
434
301
|
interviews=self._interviews,
|
435
302
|
css=css,
|
436
|
-
num_exceptions=len(self.exceptions),
|
437
303
|
performance_plot_html=performance_plot_html,
|
438
|
-
exceptions_by_type=self.exceptions_by_type,
|
439
|
-
exceptions_by_question_name=self.exceptions_by_question_name,
|
440
|
-
exceptions_by_model=self.exceptions_by_model,
|
441
|
-
models_used=models_used,
|
442
304
|
)
|
443
305
|
|
444
306
|
# Save the rendered output to a file
|
@@ -482,9 +344,3 @@ class TaskHistory:
|
|
482
344
|
|
483
345
|
if return_link:
|
484
346
|
return filename
|
485
|
-
|
486
|
-
|
487
|
-
if __name__ == "__main__":
|
488
|
-
import doctest
|
489
|
-
|
490
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
@@ -11,7 +11,6 @@ import hashlib
|
|
11
11
|
from typing import Coroutine, Any, Callable, Type, List, get_type_hints
|
12
12
|
from abc import ABC, abstractmethod
|
13
13
|
|
14
|
-
|
15
14
|
class IntendedModelCallOutcome:
|
16
15
|
"This is a tuple-like class that holds the response, cache_used, and cache_key."
|
17
16
|
|
@@ -22,7 +21,7 @@ class IntendedModelCallOutcome:
|
|
22
21
|
|
23
22
|
def __iter__(self):
|
24
23
|
"""Iterate over the class attributes.
|
25
|
-
|
24
|
+
|
26
25
|
>>> a, b, c = IntendedModelCallOutcome({'answer': "yes"}, True, 'x1289')
|
27
26
|
>>> a
|
28
27
|
{'answer': 'yes'}
|
@@ -33,11 +32,10 @@ class IntendedModelCallOutcome:
|
|
33
32
|
|
34
33
|
def __len__(self):
|
35
34
|
return 3
|
36
|
-
|
35
|
+
|
37
36
|
def __repr__(self):
|
38
37
|
return f"IntendedModelCallOutcome(response = {self.response}, cache_used = {self.cache_used}, cache_key = '{self.cache_key}')"
|
39
38
|
|
40
|
-
|
41
39
|
from edsl.config import CONFIG
|
42
40
|
|
43
41
|
from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
|
@@ -124,11 +122,6 @@ class LanguageModel(
|
|
124
122
|
# Skip the API key check. Sometimes this is useful for testing.
|
125
123
|
self._api_token = None
|
126
124
|
|
127
|
-
def ask_question(self, question):
|
128
|
-
user_prompt = question.get_instructions().render(question.data).text
|
129
|
-
system_prompt = "You are a helpful agent pretending to be a human."
|
130
|
-
return self.execute_model_call(user_prompt, system_prompt)
|
131
|
-
|
132
125
|
@property
|
133
126
|
def api_token(self) -> str:
|
134
127
|
if not hasattr(self, "_api_token"):
|
@@ -209,6 +202,8 @@ class LanguageModel(
|
|
209
202
|
"""
|
210
203
|
self._set_rate_limits(rpm=rpm, tpm=tpm)
|
211
204
|
|
205
|
+
|
206
|
+
|
212
207
|
def _set_rate_limits(self, rpm=None, tpm=None) -> None:
|
213
208
|
"""Set the rate limits for the model.
|
214
209
|
|
@@ -249,16 +244,14 @@ class LanguageModel(
|
|
249
244
|
>>> LanguageModel._overide_default_parameters(passed_parameter_dict={"temperature": 0.5}, default_parameter_dict={"temperature":0.9, "max_tokens": 1000})
|
250
245
|
{'temperature': 0.5, 'max_tokens': 1000}
|
251
246
|
"""
|
252
|
-
#
|
253
|
-
|
254
|
-
return {
|
255
|
-
parameter_name: passed_parameter_dict.get(parameter_name, default_value)
|
256
|
-
for parameter_name, default_value in default_parameter_dict.items()
|
257
|
-
}
|
247
|
+
#parameters = dict({})
|
258
248
|
|
259
|
-
|
249
|
+
return {parameter_name: passed_parameter_dict.get(parameter_name, default_value)
|
250
|
+
for parameter_name, default_value in default_parameter_dict.items()}
|
251
|
+
|
252
|
+
def __call__(self, user_prompt:str, system_prompt:str):
|
260
253
|
return self.execute_model_call(user_prompt, system_prompt)
|
261
|
-
|
254
|
+
|
262
255
|
@abstractmethod
|
263
256
|
async def async_execute_model_call(user_prompt: str, system_prompt: str):
|
264
257
|
"""Execute the model call and returns a coroutine.
|
@@ -317,10 +310,8 @@ class LanguageModel(
|
|
317
310
|
data["choices[0]"]["message"]["content"].
|
318
311
|
"""
|
319
312
|
raise NotImplementedError
|
320
|
-
|
321
|
-
async def _async_prepare_response(
|
322
|
-
self, model_call_outcome: IntendedModelCallOutcome, cache: "Cache"
|
323
|
-
) -> dict:
|
313
|
+
|
314
|
+
async def _async_prepare_response(self, model_call_outcome: IntendedModelCallOutcome, cache: "Cache") -> dict:
|
324
315
|
"""Prepare the response for return."""
|
325
316
|
|
326
317
|
model_response = {
|
@@ -330,19 +321,21 @@ class LanguageModel(
|
|
330
321
|
"raw_model_response": model_call_outcome.response,
|
331
322
|
}
|
332
323
|
|
333
|
-
answer_portion = self.parse_response(model_call_outcome.response)
|
324
|
+
answer_portion = self.parse_response(model_call_outcome.response)
|
334
325
|
try:
|
335
326
|
answer_dict = json.loads(answer_portion)
|
336
327
|
except json.JSONDecodeError as e:
|
337
328
|
# TODO: Turn into logs to generate issues
|
338
329
|
answer_dict, success = await repair(
|
339
|
-
bad_json=answer_portion,
|
330
|
+
bad_json=answer_portion,
|
331
|
+
error_message=str(e),
|
332
|
+
cache=cache
|
340
333
|
)
|
341
334
|
if not success:
|
342
335
|
raise Exception(
|
343
336
|
f"""Even the repair failed. The error was: {e}. The response was: {answer_portion}."""
|
344
337
|
)
|
345
|
-
|
338
|
+
|
346
339
|
return {**model_response, **answer_dict}
|
347
340
|
|
348
341
|
async def async_get_raw_response(
|
@@ -354,18 +347,16 @@ class LanguageModel(
|
|
354
347
|
encoded_image=None,
|
355
348
|
) -> IntendedModelCallOutcome:
|
356
349
|
import warnings
|
357
|
-
|
358
|
-
warnings.warn(
|
359
|
-
"This method is deprecated. Use async_get_intended_model_call_outcome."
|
360
|
-
)
|
350
|
+
warnings.warn("This method is deprecated. Use async_get_intended_model_call_outcome.")
|
361
351
|
return await self._async_get_intended_model_call_outcome(
|
362
352
|
user_prompt=user_prompt,
|
363
353
|
system_prompt=system_prompt,
|
364
354
|
cache=cache,
|
365
355
|
iteration=iteration,
|
366
|
-
encoded_image=encoded_image
|
356
|
+
encoded_image=encoded_image
|
367
357
|
)
|
368
358
|
|
359
|
+
|
369
360
|
async def _async_get_intended_model_call_outcome(
|
370
361
|
self,
|
371
362
|
user_prompt: str,
|
@@ -407,8 +398,8 @@ class LanguageModel(
|
|
407
398
|
"iteration": iteration,
|
408
399
|
}
|
409
400
|
cached_response, cache_key = cache.fetch(**cache_call_params)
|
410
|
-
|
411
|
-
if cache_used := cached_response is not None:
|
401
|
+
|
402
|
+
if (cache_used := cached_response is not None):
|
412
403
|
response = json.loads(cached_response)
|
413
404
|
else:
|
414
405
|
f = (
|
@@ -416,24 +407,16 @@ class LanguageModel(
|
|
416
407
|
if hasattr(self, "remote") and self.remote
|
417
408
|
else self.async_execute_model_call
|
418
409
|
)
|
419
|
-
params = {
|
420
|
-
|
421
|
-
"system_prompt": system_prompt,
|
422
|
-
**({"encoded_image": encoded_image} if encoded_image else {}),
|
410
|
+
params = {"user_prompt": user_prompt, "system_prompt": system_prompt,
|
411
|
+
**({"encoded_image": encoded_image} if encoded_image else {})
|
423
412
|
}
|
424
413
|
response = await f(**params)
|
425
|
-
new_cache_key = cache.store(
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
return IntendedModelCallOutcome(
|
431
|
-
response=response, cache_used=cache_used, cache_key=cache_key
|
432
|
-
)
|
414
|
+
new_cache_key = cache.store(**cache_call_params, response=response) # store the response in the cache
|
415
|
+
assert new_cache_key == cache_key # should be the same
|
416
|
+
|
417
|
+
return IntendedModelCallOutcome(response = response, cache_used = cache_used, cache_key = cache_key)
|
433
418
|
|
434
|
-
_get_intended_model_call_outcome = sync_wrapper(
|
435
|
-
_async_get_intended_model_call_outcome
|
436
|
-
)
|
419
|
+
_get_intended_model_call_outcome = sync_wrapper(_async_get_intended_model_call_outcome)
|
437
420
|
|
438
421
|
get_raw_response = sync_wrapper(async_get_raw_response)
|
439
422
|
|
@@ -454,7 +437,7 @@ class LanguageModel(
|
|
454
437
|
self,
|
455
438
|
user_prompt: str,
|
456
439
|
system_prompt: str,
|
457
|
-
cache:
|
440
|
+
cache: 'Cache',
|
458
441
|
iteration: int = 1,
|
459
442
|
encoded_image=None,
|
460
443
|
) -> dict:
|
@@ -472,8 +455,8 @@ class LanguageModel(
|
|
472
455
|
"system_prompt": system_prompt,
|
473
456
|
"iteration": iteration,
|
474
457
|
"cache": cache,
|
475
|
-
**({"encoded_image": encoded_image} if encoded_image else {})
|
476
|
-
}
|
458
|
+
**({"encoded_image": encoded_image} if encoded_image else {})
|
459
|
+
}
|
477
460
|
model_call_outcome = await self._async_get_intended_model_call_outcome(**params)
|
478
461
|
return await self._async_prepare_response(model_call_outcome, cache=cache)
|
479
462
|
|
edsl/language_models/registry.py
CHANGED
@@ -36,10 +36,6 @@ class Model(metaclass=Meta):
|
|
36
36
|
from edsl.inference_services.registry import default
|
37
37
|
|
38
38
|
registry = registry or default
|
39
|
-
|
40
|
-
if isinstance(model_name, int):
|
41
|
-
model_name = cls.available(name_only=True)[model_name]
|
42
|
-
|
43
39
|
factory = registry.create_model_factory(model_name)
|
44
40
|
return factory(*args, **kwargs)
|
45
41
|
|
@@ -96,7 +96,7 @@ class QuestionMultipleChoice(QuestionBase):
|
|
96
96
|
question_option_key = list(meta.find_undeclared_variables(parsed_content))[
|
97
97
|
0
|
98
98
|
]
|
99
|
-
#
|
99
|
+
#breakpoint()
|
100
100
|
translated_options = scenario.get(question_option_key)
|
101
101
|
else:
|
102
102
|
translated_options = [
|
@@ -26,6 +26,7 @@ class QuestionNumerical(QuestionBase):
|
|
26
26
|
|
27
27
|
:param question_name: The name of the question.
|
28
28
|
:param question_text: The text of the question.
|
29
|
+
:param instructions: Instructions for the question. If not provided, the default instructions are used. To view them, run `QuestionNumerical.default_instructions`.
|
29
30
|
:param min_value: The minimum value of the answer.
|
30
31
|
:param max_value: The maximum value of the answer.
|
31
32
|
"""
|
@@ -27,10 +27,6 @@ class DatasetExportMixin:
|
|
27
27
|
>>> d.relevant_columns(remove_prefix=True)
|
28
28
|
['b']
|
29
29
|
|
30
|
-
>>> d = Dataset([{'a':[1,2,3,4]}, {'b':[5,6,7,8]}])
|
31
|
-
>>> d.relevant_columns()
|
32
|
-
['a', 'b']
|
33
|
-
|
34
30
|
>>> from edsl.results import Results; Results.example().select('how_feeling', 'how_feeling_yesterday').relevant_columns()
|
35
31
|
['answer.how_feeling', 'answer.how_feeling_yesterday']
|
36
32
|
|
@@ -597,7 +593,7 @@ class DatasetExportMixin:
|
|
597
593
|
return filename
|
598
594
|
|
599
595
|
def tally(
|
600
|
-
self, *fields: Optional[str], top_n: Optional[int] = None, output="
|
596
|
+
self, *fields: Optional[str], top_n: Optional[int] = None, output="dict"
|
601
597
|
) -> Union[dict, "Dataset"]:
|
602
598
|
"""Tally the values of a field or perform a cross-tab of multiple fields.
|
603
599
|
|
@@ -605,11 +601,9 @@ class DatasetExportMixin:
|
|
605
601
|
|
606
602
|
>>> from edsl.results import Results
|
607
603
|
>>> r = Results.example()
|
608
|
-
>>> r.select('how_feeling').tally('answer.how_feeling'
|
604
|
+
>>> r.select('how_feeling').tally('answer.how_feeling')
|
609
605
|
{'OK': 2, 'Great': 1, 'Terrible': 1}
|
610
|
-
>>> r.select('how_feeling').tally('
|
611
|
-
Dataset([{'value': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
|
612
|
-
>>> r.select('how_feeling', 'period').tally('how_feeling', 'period', output = "dict")
|
606
|
+
>>> r.select('how_feeling', 'period').tally('how_feeling', 'period')
|
613
607
|
{('OK', 'morning'): 1, ('Great', 'afternoon'): 1, ('Terrible', 'morning'): 1, ('OK', 'afternoon'): 1}
|
614
608
|
"""
|
615
609
|
from collections import Counter
|
@@ -621,8 +615,6 @@ class DatasetExportMixin:
|
|
621
615
|
column.split(".")[-1] for column in self.relevant_columns()
|
622
616
|
]
|
623
617
|
|
624
|
-
# breakpoint()
|
625
|
-
|
626
618
|
if not all(
|
627
619
|
f in self.relevant_columns() or f in relevant_columns_without_prefix
|
628
620
|
for f in fields
|
@@ -649,7 +641,6 @@ class DatasetExportMixin:
|
|
649
641
|
from edsl.results.Dataset import Dataset
|
650
642
|
|
651
643
|
if output == "dict":
|
652
|
-
# why did I do this?
|
653
644
|
warnings.warn(
|
654
645
|
textwrap.dedent(
|
655
646
|
"""\
|
edsl/scenarios/Scenario.py
CHANGED
@@ -182,19 +182,6 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
182
182
|
new_scenario[key] = self[key]
|
183
183
|
return new_scenario
|
184
184
|
|
185
|
-
@classmethod
|
186
|
-
def from_url(cls, url: str, field_name: Optional[str] = "text") -> "Scenario":
|
187
|
-
"""Creates a scenario from a URL.
|
188
|
-
|
189
|
-
:param url: The URL to create the scenario from.
|
190
|
-
:param field_name: The field name to use for the text.
|
191
|
-
|
192
|
-
"""
|
193
|
-
import requests
|
194
|
-
|
195
|
-
text = requests.get(url).text
|
196
|
-
return cls({"url": url, field_name: text})
|
197
|
-
|
198
185
|
@classmethod
|
199
186
|
def from_image(cls, image_path: str) -> str:
|
200
187
|
"""Creates a scenario with a base64 encoding of an image.
|
@@ -220,7 +207,6 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
|
|
220
207
|
@classmethod
|
221
208
|
def from_pdf(cls, pdf_path):
|
222
209
|
import fitz # PyMuPDF
|
223
|
-
from edsl import Scenario
|
224
210
|
|
225
211
|
# Ensure the file exists
|
226
212
|
if not os.path.exists(pdf_path):
|
edsl/scenarios/ScenarioList.py
CHANGED
@@ -242,16 +242,6 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
242
242
|
|
243
243
|
return ScenarioList(new_data)
|
244
244
|
|
245
|
-
def from_urls(self, urls: list[str], field_name: Optional[str] = "text") -> ScenarioList:
|
246
|
-
"""Create a ScenarioList from a list of URLs.
|
247
|
-
|
248
|
-
:param urls: A list of URLs.
|
249
|
-
:param field_name: The name of the field to store the text from the URLs.
|
250
|
-
|
251
|
-
|
252
|
-
"""
|
253
|
-
return ScenarioList([Scenario.from_url(url, field_name) for url in urls])
|
254
|
-
|
255
245
|
def select(self, *fields) -> ScenarioList:
|
256
246
|
"""
|
257
247
|
Selects scenarios with only the references fields.
|
@@ -298,15 +288,12 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
|
|
298
288
|
>>> s = ScenarioList.from_list("a", [1,2,3])
|
299
289
|
>>> s.to_dataset()
|
300
290
|
Dataset([{'a': [1, 2, 3]}])
|
301
|
-
>>> s = ScenarioList.from_list("a", [1,2,3]).add_list("b", [4,5,6])
|
302
|
-
>>> s.to_dataset()
|
303
|
-
Dataset([{'a': [1, 2, 3]}, {'b': [4, 5, 6]}])
|
304
291
|
"""
|
305
292
|
from edsl.results.Dataset import Dataset
|
306
293
|
|
307
294
|
keys = self[0].keys()
|
308
|
-
data =
|
309
|
-
return Dataset(data)
|
295
|
+
data = {key: [scenario[key] for scenario in self.data] for key in keys}
|
296
|
+
return Dataset([data])
|
310
297
|
|
311
298
|
def add_list(self, name, values) -> ScenarioList:
|
312
299
|
"""Add a list of values to a ScenarioList.
|
@@ -20,24 +20,13 @@ def to_dataset(func):
|
|
20
20
|
return wrapper
|
21
21
|
|
22
22
|
|
23
|
-
def
|
24
|
-
for attr_name, attr_value in
|
25
|
-
if callable(attr_value)
|
23
|
+
def decorate_all_methods(cls):
|
24
|
+
for attr_name, attr_value in cls.__dict__.items():
|
25
|
+
if callable(attr_value):
|
26
26
|
setattr(cls, attr_name, to_dataset(attr_value))
|
27
27
|
return cls
|
28
28
|
|
29
29
|
|
30
|
-
|
31
|
-
# for attr_name, attr_value in cls.__dict__.items():
|
32
|
-
# if callable(attr_value):
|
33
|
-
# setattr(cls, attr_name, to_dataset(attr_value))
|
34
|
-
# return cls
|
35
|
-
|
36
|
-
|
37
|
-
# @decorate_all_methods
|
30
|
+
@decorate_all_methods
|
38
31
|
class ScenarioListExportMixin(DatasetExportMixin):
|
39
32
|
"""Mixin class for exporting Results objects."""
|
40
|
-
|
41
|
-
def __init_subclass__(cls, **kwargs):
|
42
|
-
super().__init_subclass__(**kwargs)
|
43
|
-
decorate_methods_from_mixin(cls, DatasetExportMixin)
|
@@ -43,9 +43,6 @@ class ScenarioListPdfMixin:
|
|
43
43
|
|
44
44
|
@staticmethod
|
45
45
|
def extract_text_from_pdf(pdf_path):
|
46
|
-
from edsl import Scenario
|
47
|
-
|
48
|
-
# TODO: Add test case
|
49
46
|
# Ensure the file exists
|
50
47
|
if not os.path.exists(pdf_path):
|
51
48
|
raise FileNotFoundError(f"The file {pdf_path} does not exist.")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edsl
|
3
|
-
Version: 0.1.31
|
3
|
+
Version: 0.1.31.dev2
|
4
4
|
Summary: Create and analyze LLM-based surveys
|
5
5
|
Home-page: https://www.expectedparrot.com/
|
6
6
|
License: MIT
|
@@ -19,7 +19,6 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
19
|
Requires-Dist: aiohttp (>=3.9.1,<4.0.0)
|
20
20
|
Requires-Dist: anthropic (>=0.23.1,<0.24.0)
|
21
21
|
Requires-Dist: black[jupyter] (>=24.4.2,<25.0.0)
|
22
|
-
Requires-Dist: groq (>=0.9.0,<0.10.0)
|
23
22
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
24
23
|
Requires-Dist: jupyter (>=1.0.0,<2.0.0)
|
25
24
|
Requires-Dist: markdown2 (>=2.4.11,<3.0.0)
|