edsl 0.1.27.dev2__py3-none-any.whl → 0.1.29__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 +107 -30
- edsl/BaseDiff.py +260 -0
- edsl/__init__.py +25 -21
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +103 -46
- edsl/agents/AgentList.py +97 -13
- edsl/agents/Invigilator.py +23 -10
- edsl/agents/InvigilatorBase.py +19 -14
- edsl/agents/PromptConstructionMixin.py +342 -100
- edsl/agents/descriptors.py +5 -2
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/conjure/AgentConstructionMixin.py +152 -0
- edsl/conjure/Conjure.py +56 -0
- edsl/conjure/InputData.py +659 -0
- edsl/conjure/InputDataCSV.py +48 -0
- edsl/conjure/InputDataMixinQuestionStats.py +182 -0
- edsl/conjure/InputDataPyRead.py +91 -0
- edsl/conjure/InputDataSPSS.py +8 -0
- edsl/conjure/InputDataStata.py +8 -0
- edsl/conjure/QuestionOptionMixin.py +76 -0
- edsl/conjure/QuestionTypeMixin.py +23 -0
- edsl/conjure/RawQuestion.py +65 -0
- edsl/conjure/SurveyResponses.py +7 -0
- edsl/conjure/__init__.py +9 -4
- edsl/conjure/examples/placeholder.txt +0 -0
- edsl/conjure/naming_utilities.py +263 -0
- edsl/conjure/utilities.py +165 -28
- edsl/conversation/Conversation.py +238 -0
- edsl/conversation/car_buying.py +58 -0
- edsl/conversation/mug_negotiation.py +81 -0
- edsl/conversation/next_speaker_utilities.py +93 -0
- edsl/coop/coop.py +337 -121
- edsl/coop/utils.py +56 -70
- edsl/data/Cache.py +74 -22
- edsl/data/CacheHandler.py +10 -9
- edsl/data/SQLiteDict.py +11 -3
- edsl/inference_services/AnthropicService.py +1 -0
- edsl/inference_services/DeepInfraService.py +20 -13
- edsl/inference_services/GoogleService.py +7 -1
- edsl/inference_services/InferenceServicesCollection.py +33 -7
- edsl/inference_services/OpenAIService.py +17 -10
- edsl/inference_services/models_available_cache.py +69 -0
- edsl/inference_services/rate_limits_cache.py +25 -0
- edsl/inference_services/write_available.py +10 -0
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +322 -73
- edsl/jobs/buckets/BucketCollection.py +9 -3
- edsl/jobs/buckets/ModelBuckets.py +4 -2
- edsl/jobs/buckets/TokenBucket.py +1 -2
- edsl/jobs/interviews/Interview.py +7 -10
- edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +39 -20
- edsl/jobs/interviews/retry_management.py +4 -4
- edsl/jobs/runners/JobsRunnerAsyncio.py +103 -65
- edsl/jobs/runners/JobsRunnerStatusData.py +3 -3
- edsl/jobs/tasks/QuestionTaskCreator.py +4 -2
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +42 -55
- edsl/language_models/ModelList.py +96 -0
- edsl/language_models/registry.py +14 -0
- edsl/language_models/repair.py +97 -25
- edsl/notebooks/Notebook.py +157 -32
- edsl/prompts/Prompt.py +31 -19
- edsl/questions/QuestionBase.py +145 -23
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +3 -3
- edsl/questions/QuestionFunctional.py +0 -3
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +16 -8
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +9 -4
- edsl/questions/question_registry.py +27 -31
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +31 -0
- edsl/results/DatasetExportMixin.py +493 -0
- edsl/results/Result.py +42 -82
- edsl/results/Results.py +178 -66
- edsl/results/ResultsDBMixin.py +10 -9
- edsl/results/ResultsExportMixin.py +23 -507
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +9 -9
- edsl/scenarios/FileStore.py +140 -0
- edsl/scenarios/Scenario.py +59 -6
- edsl/scenarios/ScenarioList.py +138 -52
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/study/ObjectEntry.py +173 -0
- edsl/study/ProofOfWork.py +113 -0
- edsl/study/SnapShot.py +73 -0
- edsl/study/Study.py +498 -0
- edsl/study/__init__.py +4 -0
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +124 -37
- edsl/surveys/SurveyExportMixin.py +25 -5
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/tools/plotting.py +4 -2
- edsl/utilities/__init__.py +21 -20
- edsl/utilities/gcp_bucket/__init__.py +0 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
- edsl/utilities/gcp_bucket/simple_example.py +9 -0
- edsl/utilities/interface.py +90 -73
- edsl/utilities/repair_functions.py +28 -0
- edsl/utilities/utilities.py +59 -6
- {edsl-0.1.27.dev2.dist-info → edsl-0.1.29.dist-info}/METADATA +42 -15
- edsl-0.1.29.dist-info/RECORD +203 -0
- edsl/conjure/RawResponseColumn.py +0 -327
- edsl/conjure/SurveyBuilder.py +0 -308
- edsl/conjure/SurveyBuilderCSV.py +0 -78
- edsl/conjure/SurveyBuilderSPSS.py +0 -118
- edsl/data/RemoteDict.py +0 -103
- edsl-0.1.27.dev2.dist-info/RECORD +0 -172
- {edsl-0.1.27.dev2.dist-info → edsl-0.1.29.dist-info}/LICENSE +0 -0
- {edsl-0.1.27.dev2.dist-info → edsl-0.1.29.dist-info}/WHEEL +0 -0
edsl/notebooks/Notebook.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
"""A Notebook is
|
1
|
+
"""A Notebook is a utility class that allows you to easily share/pull ipynbs from Coop."""
|
2
2
|
|
3
|
+
import json
|
3
4
|
from typing import Dict, List, Optional
|
4
|
-
|
5
|
+
|
6
|
+
|
5
7
|
from edsl.Base import Base
|
6
8
|
from edsl.utilities.decorators import (
|
7
9
|
add_edsl_version,
|
@@ -14,43 +16,91 @@ class Notebook(Base):
|
|
14
16
|
A Notebook is a utility class that allows you to easily share/pull ipynbs from Coop.
|
15
17
|
"""
|
16
18
|
|
17
|
-
|
19
|
+
default_name = "notebook"
|
20
|
+
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
data: Optional[Dict] = None,
|
24
|
+
path: Optional[str] = None,
|
25
|
+
name: Optional[str] = None,
|
26
|
+
):
|
18
27
|
"""
|
19
28
|
Initialize a new Notebook.
|
20
|
-
|
21
|
-
|
29
|
+
|
30
|
+
:param data: A dictionary representing the notebook data.
|
31
|
+
This dictionary must conform to the official Jupyter Notebook format, as defined by nbformat.
|
32
|
+
:param path: A filepath from which to load the notebook.
|
33
|
+
If no path is provided, assume this code is run in a notebook and try to load the current notebook from file.
|
34
|
+
:param name: A name for the Notebook.
|
22
35
|
"""
|
36
|
+
import nbformat
|
37
|
+
|
38
|
+
# Load current notebook path as fallback (VS Code only)
|
39
|
+
path = path or globals().get("__vsc_ipynb_file__")
|
23
40
|
if data is not None:
|
41
|
+
nbformat.validate(data)
|
24
42
|
self.data = data
|
25
43
|
elif path is not None:
|
26
|
-
|
27
|
-
|
28
|
-
self.data =
|
44
|
+
with open(path, mode="r", encoding="utf-8") as f:
|
45
|
+
data = nbformat.read(f, as_version=4)
|
46
|
+
self.data = json.loads(json.dumps(data))
|
29
47
|
else:
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
48
|
+
# TODO: Support for IDEs other than VSCode
|
49
|
+
raise NotImplementedError(
|
50
|
+
"Cannot create a notebook from within itself in this development environment"
|
51
|
+
)
|
34
52
|
|
35
|
-
#
|
53
|
+
# TODO: perhaps add sanity check function
|
36
54
|
# 1. could check if the notebook is a valid notebook
|
37
55
|
# 2. could check notebook uses EDSL
|
38
56
|
# ....
|
39
57
|
|
58
|
+
self.name = name or self.default_name
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def from_script(cls, path: str, name: Optional[str] = None) -> "Notebook":
|
62
|
+
# Read the script file
|
63
|
+
with open(path, "r") as script_file:
|
64
|
+
script_content = script_file.read()
|
65
|
+
|
66
|
+
# Create a new Jupyter notebook
|
67
|
+
nb = nbformat.v4.new_notebook()
|
68
|
+
|
69
|
+
# Add the script content to the first cell
|
70
|
+
first_cell = nbformat.v4.new_code_cell(script_content)
|
71
|
+
nb.cells.append(first_cell)
|
72
|
+
|
73
|
+
# Create a Notebook instance with the notebook data
|
74
|
+
notebook_instance = cls(nb)
|
75
|
+
|
76
|
+
return notebook_instance
|
77
|
+
|
78
|
+
@classmethod
|
79
|
+
def from_current_script(cls) -> "Notebook":
|
80
|
+
import inspect
|
81
|
+
import os
|
82
|
+
|
83
|
+
# Get the path to the current file
|
84
|
+
current_frame = inspect.currentframe()
|
85
|
+
caller_frame = inspect.getouterframes(current_frame, 2)
|
86
|
+
current_file_path = os.path.abspath(caller_frame[1].filename)
|
87
|
+
|
88
|
+
# Use from_script to create the notebook
|
89
|
+
return cls.from_script(current_file_path)
|
90
|
+
|
40
91
|
def __eq__(self, other):
|
41
92
|
"""
|
42
93
|
Check if two Notebooks are equal.
|
94
|
+
This only checks the notebook data.
|
43
95
|
"""
|
44
96
|
return self.data == other.data
|
45
97
|
|
46
98
|
@add_edsl_version
|
47
99
|
def to_dict(self) -> dict:
|
48
100
|
"""
|
49
|
-
Convert to a dictionary.
|
50
|
-
AF: here you will create a dict from which self.from_dict can recreate the object.
|
51
|
-
AF: the decorator will add the edsl_version to the dict.
|
101
|
+
Convert a Notebook to a dictionary.
|
52
102
|
"""
|
53
|
-
return {"data": self.data}
|
103
|
+
return {"name": self.name, "data": self.data}
|
54
104
|
|
55
105
|
@classmethod
|
56
106
|
@remove_edsl_version
|
@@ -58,12 +108,17 @@ class Notebook(Base):
|
|
58
108
|
"""
|
59
109
|
Convert a dictionary representation of a Notebook to a Notebook object.
|
60
110
|
"""
|
61
|
-
return cls(data=d["data"])
|
111
|
+
return cls(data=d["data"], name=d["name"])
|
112
|
+
|
113
|
+
def to_file(self, path: str):
|
114
|
+
"""
|
115
|
+
Save the notebook at the specified filepath.
|
116
|
+
"""
|
117
|
+
nbformat.write(nbformat.from_dict(self.data), fp=path)
|
62
118
|
|
63
119
|
def print(self):
|
64
120
|
"""
|
65
121
|
Print the notebook.
|
66
|
-
AF: not sure how this should behave for a notebook
|
67
122
|
"""
|
68
123
|
from rich import print_json
|
69
124
|
import json
|
@@ -72,45 +127,115 @@ class Notebook(Base):
|
|
72
127
|
|
73
128
|
def __repr__(self):
|
74
129
|
"""
|
75
|
-
|
130
|
+
Return representation of Notebook.
|
76
131
|
"""
|
77
|
-
return f
|
132
|
+
return f'Notebook(data={self.data}, name="""{self.name}""")'
|
78
133
|
|
79
134
|
def _repr_html_(self):
|
80
135
|
"""
|
81
|
-
|
136
|
+
Return HTML representation of Notebook.
|
137
|
+
"""
|
138
|
+
from nbconvert import HTMLExporter
|
139
|
+
import nbformat
|
140
|
+
|
141
|
+
notebook = nbformat.from_dict(self.data)
|
142
|
+
html_exporter = HTMLExporter(template_name="basic")
|
143
|
+
(body, _) = html_exporter.from_notebook_node(notebook)
|
144
|
+
return body
|
145
|
+
|
146
|
+
def _table(self) -> tuple[dict, list]:
|
147
|
+
"""
|
148
|
+
Prepare generic table data.
|
82
149
|
"""
|
83
|
-
|
150
|
+
table_data = []
|
151
|
+
|
152
|
+
notebook_preview = ""
|
153
|
+
for cell in self.data["cells"]:
|
154
|
+
if "source" in cell:
|
155
|
+
notebook_preview += f"{cell['source']}\n"
|
156
|
+
if len(notebook_preview) > 1000:
|
157
|
+
notebook_preview = f"{notebook_preview[:1000]} [...]"
|
158
|
+
break
|
159
|
+
notebook_preview = notebook_preview.rstrip()
|
160
|
+
|
161
|
+
table_data.append(
|
162
|
+
{
|
163
|
+
"Attribute": "name",
|
164
|
+
"Value": repr(self.name),
|
165
|
+
}
|
166
|
+
)
|
167
|
+
table_data.append(
|
168
|
+
{
|
169
|
+
"Attribute": "notebook_preview",
|
170
|
+
"Value": notebook_preview,
|
171
|
+
}
|
172
|
+
)
|
84
173
|
|
85
|
-
|
174
|
+
column_names = ["Attribute", "Value"]
|
175
|
+
return table_data, column_names
|
86
176
|
|
87
177
|
def rich_print(self) -> "Table":
|
88
178
|
"""
|
89
|
-
|
179
|
+
Display a Notebook as a rich table.
|
90
180
|
"""
|
91
|
-
|
181
|
+
from rich.table import Table
|
182
|
+
|
183
|
+
table_data, column_names = self._table()
|
184
|
+
table = Table(title=f"{self.__class__.__name__} Attributes")
|
185
|
+
for column in column_names:
|
186
|
+
table.add_column(column, style="bold")
|
187
|
+
|
188
|
+
for row in table_data:
|
189
|
+
row_data = [row[column] for column in column_names]
|
190
|
+
table.add_row(*row_data)
|
191
|
+
|
192
|
+
return table
|
92
193
|
|
93
194
|
@classmethod
|
94
195
|
def example(cls) -> "Notebook":
|
95
196
|
"""
|
96
197
|
Return an example Notebook.
|
97
|
-
AF: add a simple custom example here
|
98
198
|
"""
|
99
|
-
|
199
|
+
cells = [
|
200
|
+
{
|
201
|
+
"cell_type": "markdown",
|
202
|
+
"metadata": dict(),
|
203
|
+
"source": "# Test notebook",
|
204
|
+
},
|
205
|
+
{
|
206
|
+
"cell_type": "code",
|
207
|
+
"execution_count": 1,
|
208
|
+
"metadata": dict(),
|
209
|
+
"outputs": [
|
210
|
+
{
|
211
|
+
"name": "stdout",
|
212
|
+
"output_type": "stream",
|
213
|
+
"text": "Hello world!\n",
|
214
|
+
}
|
215
|
+
],
|
216
|
+
"source": 'print("Hello world!")',
|
217
|
+
},
|
218
|
+
]
|
219
|
+
data = {
|
220
|
+
"metadata": dict(),
|
221
|
+
"nbformat": 4,
|
222
|
+
"nbformat_minor": 4,
|
223
|
+
"cells": cells,
|
224
|
+
}
|
225
|
+
return cls(data=data)
|
100
226
|
|
101
227
|
def code(self) -> List[str]:
|
102
228
|
"""
|
103
229
|
Return the code that could be used to create this Notebook.
|
104
|
-
AF: Again, not sure
|
105
230
|
"""
|
106
231
|
lines = []
|
107
|
-
lines.append("from edsl
|
108
|
-
lines.append(f
|
232
|
+
lines.append("from edsl import Notebook")
|
233
|
+
lines.append(f'nb = Notebook(data={self.data}, name="""{self.name}""")')
|
109
234
|
return lines
|
110
235
|
|
111
236
|
|
112
237
|
if __name__ == "__main__":
|
113
|
-
from edsl
|
238
|
+
from edsl import Notebook
|
114
239
|
|
115
240
|
notebook = Notebook.example()
|
116
241
|
assert notebook == notebook.from_dict(notebook.to_dict())
|
edsl/prompts/Prompt.py
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
"""Class for creating prompts to be used in a survey."""
|
2
|
-
|
3
1
|
from __future__ import annotations
|
4
2
|
from typing import Optional
|
5
3
|
from abc import ABC
|
6
4
|
from typing import Any, List
|
7
5
|
|
8
6
|
from rich.table import Table
|
9
|
-
from jinja2 import Template, Environment, meta, TemplateSyntaxError
|
7
|
+
from jinja2 import Template, Environment, meta, TemplateSyntaxError, Undefined
|
8
|
+
|
9
|
+
|
10
|
+
class PreserveUndefined(Undefined):
|
11
|
+
def __str__(self):
|
12
|
+
return "{{ " + self._undefined_name + " }}"
|
13
|
+
|
10
14
|
|
11
15
|
from edsl.exceptions.prompts import TemplateRenderError
|
12
16
|
from edsl.prompts.prompt_config import (
|
@@ -35,6 +39,10 @@ class PromptBase(
|
|
35
39
|
|
36
40
|
return data_to_html(self.to_dict())
|
37
41
|
|
42
|
+
def __len__(self):
|
43
|
+
"""Return the length of the prompt text."""
|
44
|
+
return len(self.text)
|
45
|
+
|
38
46
|
@classmethod
|
39
47
|
def prompt_attributes(cls) -> List[str]:
|
40
48
|
"""Return the prompt class attributes."""
|
@@ -75,10 +83,10 @@ class PromptBase(
|
|
75
83
|
>>> p = Prompt("Hello, {{person}}")
|
76
84
|
>>> p2 = Prompt("How are you?")
|
77
85
|
>>> p + p2
|
78
|
-
Prompt(text
|
86
|
+
Prompt(text=\"""Hello, {{person}}How are you?\""")
|
79
87
|
|
80
88
|
>>> p + "How are you?"
|
81
|
-
Prompt(text
|
89
|
+
Prompt(text=\"""Hello, {{person}}How are you?\""")
|
82
90
|
"""
|
83
91
|
if isinstance(other_prompt, str):
|
84
92
|
return self.__class__(self.text + other_prompt)
|
@@ -114,7 +122,7 @@ class PromptBase(
|
|
114
122
|
Example:
|
115
123
|
>>> p = Prompt("Hello, {{person}}")
|
116
124
|
>>> p
|
117
|
-
Prompt(text
|
125
|
+
Prompt(text=\"""Hello, {{person}}\""")
|
118
126
|
"""
|
119
127
|
return f'Prompt(text="""{self.text}""")'
|
120
128
|
|
@@ -137,7 +145,7 @@ class PromptBase(
|
|
137
145
|
:param template: The template to find the variables in.
|
138
146
|
|
139
147
|
"""
|
140
|
-
env = Environment()
|
148
|
+
env = Environment(undefined=PreserveUndefined)
|
141
149
|
ast = env.parse(template)
|
142
150
|
return list(meta.find_undeclared_variables(ast))
|
143
151
|
|
@@ -186,13 +194,16 @@ class PromptBase(
|
|
186
194
|
|
187
195
|
>>> p = Prompt("Hello, {{person}}")
|
188
196
|
>>> p.render({"person": "John"})
|
189
|
-
|
197
|
+
Prompt(text=\"""Hello, John\""")
|
190
198
|
|
191
199
|
>>> p.render({"person": "Mr. {{last_name}}", "last_name": "Horton"})
|
192
|
-
|
200
|
+
Prompt(text=\"""Hello, Mr. Horton\""")
|
193
201
|
|
194
202
|
>>> p.render({"person": "Mr. {{last_name}}", "last_name": "Ho{{letter}}ton"}, max_nesting = 1)
|
195
|
-
|
203
|
+
Prompt(text=\"""Hello, Mr. Ho{{ letter }}ton\""")
|
204
|
+
|
205
|
+
>>> p.render({"person": "Mr. {{last_name}}"})
|
206
|
+
Prompt(text=\"""Hello, Mr. {{ last_name }}\""")
|
196
207
|
"""
|
197
208
|
new_text = self._render(
|
198
209
|
self.text, primary_replacement, **additional_replacements
|
@@ -216,12 +227,13 @@ class PromptBase(
|
|
216
227
|
>>> codebook = {"age": "Age"}
|
217
228
|
>>> p = Prompt("You are an agent named {{ name }}. {{ codebook['age']}}: {{ age }}")
|
218
229
|
>>> p.render({"name": "John", "age": 44}, codebook=codebook)
|
219
|
-
|
230
|
+
Prompt(text=\"""You are an agent named John. Age: 44\""")
|
220
231
|
"""
|
232
|
+
env = Environment(undefined=PreserveUndefined)
|
221
233
|
try:
|
222
234
|
previous_text = None
|
223
235
|
for _ in range(MAX_NESTING):
|
224
|
-
rendered_text =
|
236
|
+
rendered_text = env.from_string(text).render(
|
225
237
|
primary_replacement, **additional_replacements
|
226
238
|
)
|
227
239
|
if rendered_text == previous_text:
|
@@ -258,7 +270,7 @@ class PromptBase(
|
|
258
270
|
>>> p = Prompt("Hello, {{person}}")
|
259
271
|
>>> p2 = Prompt.from_dict(p.to_dict())
|
260
272
|
>>> p2
|
261
|
-
Prompt(text
|
273
|
+
Prompt(text=\"""Hello, {{person}}\""")
|
262
274
|
|
263
275
|
"""
|
264
276
|
class_name = data["class_name"]
|
@@ -290,6 +302,12 @@ class Prompt(PromptBase):
|
|
290
302
|
component_type = ComponentTypes.GENERIC
|
291
303
|
|
292
304
|
|
305
|
+
if __name__ == "__main__":
|
306
|
+
print("Running doctests...")
|
307
|
+
import doctest
|
308
|
+
|
309
|
+
doctest.testmod()
|
310
|
+
|
293
311
|
from edsl.prompts.library.question_multiple_choice import *
|
294
312
|
from edsl.prompts.library.agent_instructions import *
|
295
313
|
from edsl.prompts.library.agent_persona import *
|
@@ -302,9 +320,3 @@ from edsl.prompts.library.question_numerical import *
|
|
302
320
|
from edsl.prompts.library.question_rank import *
|
303
321
|
from edsl.prompts.library.question_extract import *
|
304
322
|
from edsl.prompts.library.question_list import *
|
305
|
-
|
306
|
-
|
307
|
-
if __name__ == "__main__":
|
308
|
-
import doctest
|
309
|
-
|
310
|
-
doctest.testmod()
|