edsl 0.1.29__py3-none-any.whl → 0.1.29.dev1__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 +18 -18
- edsl/__init__.py +24 -24
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +41 -77
- edsl/agents/AgentList.py +6 -35
- edsl/agents/Invigilator.py +1 -19
- edsl/agents/InvigilatorBase.py +10 -15
- edsl/agents/PromptConstructionMixin.py +100 -342
- edsl/agents/descriptors.py +1 -2
- edsl/config.py +1 -2
- edsl/conjure/InputData.py +8 -39
- edsl/coop/coop.py +150 -187
- edsl/coop/utils.py +75 -43
- edsl/data/Cache.py +5 -19
- edsl/data/SQLiteDict.py +3 -11
- edsl/jobs/Answers.py +1 -15
- edsl/jobs/Jobs.py +46 -90
- edsl/jobs/buckets/ModelBuckets.py +2 -4
- edsl/jobs/buckets/TokenBucket.py +2 -1
- edsl/jobs/interviews/Interview.py +9 -3
- edsl/jobs/interviews/InterviewStatusMixin.py +3 -3
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +10 -15
- edsl/jobs/runners/JobsRunnerAsyncio.py +25 -21
- edsl/jobs/tasks/TaskHistory.py +3 -4
- edsl/language_models/LanguageModel.py +11 -5
- edsl/language_models/ModelList.py +3 -3
- edsl/language_models/repair.py +7 -8
- edsl/notebooks/Notebook.py +3 -40
- edsl/prompts/Prompt.py +19 -31
- edsl/questions/QuestionBase.py +13 -38
- edsl/questions/QuestionBudget.py +6 -5
- edsl/questions/QuestionCheckBox.py +3 -7
- edsl/questions/QuestionExtract.py +3 -5
- edsl/questions/QuestionFreeText.py +3 -3
- edsl/questions/QuestionFunctional.py +3 -0
- edsl/questions/QuestionList.py +4 -3
- edsl/questions/QuestionMultipleChoice.py +8 -16
- edsl/questions/QuestionNumerical.py +3 -4
- edsl/questions/QuestionRank.py +3 -5
- edsl/questions/__init__.py +3 -4
- edsl/questions/descriptors.py +2 -4
- edsl/questions/question_registry.py +31 -20
- edsl/questions/settings.py +1 -1
- edsl/results/Dataset.py +0 -31
- edsl/results/Result.py +74 -22
- edsl/results/Results.py +47 -97
- edsl/results/ResultsDBMixin.py +3 -7
- edsl/results/ResultsExportMixin.py +537 -22
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +5 -5
- edsl/scenarios/Scenario.py +6 -5
- edsl/scenarios/ScenarioList.py +11 -34
- edsl/scenarios/ScenarioListPdfMixin.py +1 -2
- edsl/scenarios/__init__.py +0 -1
- edsl/study/ObjectEntry.py +13 -89
- edsl/study/ProofOfWork.py +2 -5
- edsl/study/SnapShot.py +8 -4
- edsl/study/Study.py +14 -21
- edsl/study/__init__.py +0 -2
- edsl/surveys/MemoryPlan.py +4 -11
- edsl/surveys/Survey.py +7 -46
- edsl/surveys/SurveyExportMixin.py +2 -4
- edsl/surveys/SurveyFlowVisualizationMixin.py +4 -6
- edsl/tools/plotting.py +2 -4
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +45 -66
- edsl/utilities/utilities.py +13 -11
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/METADATA +10 -11
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/RECORD +72 -75
- edsl-0.1.29.dev1.dist-info/entry_points.txt +3 -0
- edsl/base/Base.py +0 -289
- edsl/results/DatasetExportMixin.py +0 -493
- edsl/scenarios/FileStore.py +0 -140
- edsl/scenarios/ScenarioListExportMixin.py +0 -32
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/LICENSE +0 -0
- {edsl-0.1.29.dist-info → edsl-0.1.29.dev1.dist-info}/WHEEL +0 -0
@@ -1,17 +1,29 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import time
|
3
3
|
import asyncio
|
4
|
-
import
|
4
|
+
import textwrap
|
5
5
|
from contextlib import contextmanager
|
6
6
|
|
7
7
|
from typing import Coroutine, List, AsyncGenerator, Optional, Union
|
8
8
|
|
9
|
+
from rich.live import Live
|
10
|
+
from rich.console import Console
|
11
|
+
|
9
12
|
from edsl import shared_globals
|
13
|
+
from edsl.results import Results, Result
|
14
|
+
|
10
15
|
from edsl.jobs.interviews.Interview import Interview
|
16
|
+
from edsl.utilities.decorators import jupyter_nb_handler
|
17
|
+
|
18
|
+
# from edsl.jobs.Jobs import Jobs
|
11
19
|
from edsl.jobs.runners.JobsRunnerStatusMixin import JobsRunnerStatusMixin
|
20
|
+
from edsl.language_models import LanguageModel
|
21
|
+
from edsl.data.Cache import Cache
|
22
|
+
|
12
23
|
from edsl.jobs.tasks.TaskHistory import TaskHistory
|
13
24
|
from edsl.jobs.buckets.BucketCollection import BucketCollection
|
14
|
-
|
25
|
+
|
26
|
+
import time
|
15
27
|
|
16
28
|
|
17
29
|
class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
@@ -30,13 +42,13 @@ class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
|
30
42
|
|
31
43
|
async def run_async_generator(
|
32
44
|
self,
|
33
|
-
cache:
|
45
|
+
cache: Cache,
|
34
46
|
n: int = 1,
|
35
47
|
debug: bool = False,
|
36
48
|
stop_on_exception: bool = False,
|
37
49
|
sidecar_model: "LanguageModel" = None,
|
38
50
|
total_interviews: Optional[List["Interview"]] = None,
|
39
|
-
) -> AsyncGenerator[
|
51
|
+
) -> AsyncGenerator[Result, None]:
|
40
52
|
"""Creates the tasks, runs them asynchronously, and returns the results as a Results object.
|
41
53
|
|
42
54
|
Completed tasks are yielded as they are completed.
|
@@ -143,21 +155,19 @@ class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
|
143
155
|
|
144
156
|
prompt_dictionary = {}
|
145
157
|
for answer_key_name in answer_key_names:
|
146
|
-
prompt_dictionary[
|
147
|
-
|
148
|
-
|
149
|
-
prompt_dictionary[
|
150
|
-
|
151
|
-
|
158
|
+
prompt_dictionary[
|
159
|
+
answer_key_name + "_user_prompt"
|
160
|
+
] = question_name_to_prompts[answer_key_name]["user_prompt"]
|
161
|
+
prompt_dictionary[
|
162
|
+
answer_key_name + "_system_prompt"
|
163
|
+
] = question_name_to_prompts[answer_key_name]["system_prompt"]
|
152
164
|
|
153
165
|
raw_model_results_dictionary = {}
|
154
166
|
for result in valid_results:
|
155
167
|
question_name = result["question_name"]
|
156
|
-
raw_model_results_dictionary[
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
from edsl.results.Result import Result
|
168
|
+
raw_model_results_dictionary[
|
169
|
+
question_name + "_raw_model_response"
|
170
|
+
] = result["raw_model_response"]
|
161
171
|
|
162
172
|
result = Result(
|
163
173
|
agent=interview.agent,
|
@@ -187,8 +197,6 @@ class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
|
187
197
|
print_exceptions: bool = True,
|
188
198
|
) -> "Coroutine":
|
189
199
|
"""Runs a collection of interviews, handling both async and sync contexts."""
|
190
|
-
from rich.console import Console
|
191
|
-
|
192
200
|
console = Console()
|
193
201
|
self.results = []
|
194
202
|
self.start_time = time.monotonic()
|
@@ -196,8 +204,6 @@ class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
|
196
204
|
self.cache = cache
|
197
205
|
self.sidecar_model = sidecar_model
|
198
206
|
|
199
|
-
from edsl.results.Results import Results
|
200
|
-
|
201
207
|
if not progress_bar:
|
202
208
|
# print("Running without progress bar")
|
203
209
|
with cache as c:
|
@@ -219,8 +225,6 @@ class JobsRunnerAsyncio(JobsRunnerStatusMixin):
|
|
219
225
|
results = Results(survey=self.jobs.survey, data=self.results)
|
220
226
|
else:
|
221
227
|
# print("Running with progress bar")
|
222
|
-
from rich.live import Live
|
223
|
-
from rich.console import Console
|
224
228
|
|
225
229
|
def generate_table():
|
226
230
|
return self.status_table(self.results, self.elapsed_time)
|
edsl/jobs/tasks/TaskHistory.py
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
from edsl.jobs.tasks.task_status_enum import TaskStatus
|
2
|
+
from matplotlib import pyplot as plt
|
2
3
|
from typing import List, Optional
|
4
|
+
|
5
|
+
import matplotlib.pyplot as plt
|
3
6
|
from io import BytesIO
|
4
7
|
import base64
|
5
8
|
|
@@ -72,8 +75,6 @@ class TaskHistory:
|
|
72
75
|
|
73
76
|
def plot_completion_times(self):
|
74
77
|
"""Plot the completion times for each task."""
|
75
|
-
import matplotlib.pyplot as plt
|
76
|
-
|
77
78
|
updates = self.get_updates()
|
78
79
|
|
79
80
|
elapsed = [update.max_time - update.min_time for update in updates]
|
@@ -125,8 +126,6 @@ class TaskHistory:
|
|
125
126
|
rows = int(len(TaskStatus) ** 0.5) + 1
|
126
127
|
cols = (len(TaskStatus) + rows - 1) // rows # Ensure all plots fit
|
127
128
|
|
128
|
-
import matplotlib.pyplot as plt
|
129
|
-
|
130
129
|
fig, axes = plt.subplots(rows, cols, figsize=(15, 10))
|
131
130
|
axes = axes.flatten() # Flatten in case of a single row/column
|
132
131
|
|
@@ -7,18 +7,26 @@ import asyncio
|
|
7
7
|
import json
|
8
8
|
import time
|
9
9
|
import os
|
10
|
+
|
10
11
|
from typing import Coroutine, Any, Callable, Type, List, get_type_hints
|
11
|
-
|
12
|
+
|
13
|
+
from abc import ABC, abstractmethod, ABCMeta
|
14
|
+
|
15
|
+
from rich.table import Table
|
12
16
|
|
13
17
|
from edsl.config import CONFIG
|
14
18
|
|
19
|
+
from edsl.utilities.utilities import clean_json
|
15
20
|
from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
|
16
21
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
17
|
-
|
18
22
|
from edsl.language_models.repair import repair
|
23
|
+
from edsl.exceptions.language_models import LanguageModelAttributeTypeError
|
19
24
|
from edsl.enums import InferenceServiceType
|
20
25
|
from edsl.Base import RichPrintingMixin, PersistenceMixin
|
26
|
+
from edsl.data.Cache import Cache
|
21
27
|
from edsl.enums import service_to_api_keyname
|
28
|
+
|
29
|
+
|
22
30
|
from edsl.exceptions import MissingAPIKeyError
|
23
31
|
from edsl.language_models.RegisterLanguageModelsMeta import RegisterLanguageModelsMeta
|
24
32
|
|
@@ -283,7 +291,7 @@ class LanguageModel(
|
|
283
291
|
self,
|
284
292
|
user_prompt: str,
|
285
293
|
system_prompt: str,
|
286
|
-
cache
|
294
|
+
cache,
|
287
295
|
iteration: int = 0,
|
288
296
|
encoded_image=None,
|
289
297
|
) -> tuple[dict, bool, str]:
|
@@ -482,8 +490,6 @@ class LanguageModel(
|
|
482
490
|
|
483
491
|
def rich_print(self):
|
484
492
|
"""Display an object as a table."""
|
485
|
-
from rich.table import Table
|
486
|
-
|
487
493
|
table = Table(title="Language Model")
|
488
494
|
table.add_column("Attribute", style="bold")
|
489
495
|
table.add_column("Value")
|
@@ -5,7 +5,7 @@ from edsl import Model
|
|
5
5
|
from edsl.language_models import LanguageModel
|
6
6
|
from edsl.Base import Base
|
7
7
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
8
|
-
from edsl.utilities
|
8
|
+
from edsl.utilities import is_valid_variable_name
|
9
9
|
from edsl.utilities.utilities import dict_hash
|
10
10
|
|
11
11
|
|
@@ -56,11 +56,11 @@ class ModelList(Base, UserList):
|
|
56
56
|
return {"models": [model._to_dict() for model in self]}
|
57
57
|
|
58
58
|
@classmethod
|
59
|
-
def from_names(self, *args
|
59
|
+
def from_names(self, *args):
|
60
60
|
"""A a model list from a list of names"""
|
61
61
|
if len(args) == 1 and isinstance(args[0], list):
|
62
62
|
args = args[0]
|
63
|
-
return ModelList([Model(model_name
|
63
|
+
return ModelList([Model(model_name) for model_name in args])
|
64
64
|
|
65
65
|
@add_edsl_version
|
66
66
|
def to_dict(self):
|
edsl/language_models/repair.py
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
import json
|
2
2
|
import asyncio
|
3
3
|
import warnings
|
4
|
+
from rich import print
|
5
|
+
from rich.console import Console
|
6
|
+
from rich.syntax import Syntax
|
7
|
+
|
8
|
+
from edsl.utilities.utilities import clean_json
|
9
|
+
|
10
|
+
from edsl.utilities.repair_functions import extract_json_from_string
|
4
11
|
|
5
12
|
|
6
13
|
async def async_repair(
|
7
14
|
bad_json, error_message="", user_prompt=None, system_prompt=None, cache=None
|
8
15
|
):
|
9
|
-
from edsl.utilities.utilities import clean_json
|
10
|
-
|
11
16
|
s = clean_json(bad_json)
|
12
17
|
|
13
18
|
try:
|
@@ -22,8 +27,6 @@ async def async_repair(
|
|
22
27
|
return valid_dict, success
|
23
28
|
|
24
29
|
try:
|
25
|
-
from edsl.utilities.repair_functions import extract_json_from_string
|
26
|
-
|
27
30
|
valid_dict = extract_json_from_string(s)
|
28
31
|
success = True
|
29
32
|
except ValueError:
|
@@ -95,10 +98,6 @@ async def async_repair(
|
|
95
98
|
except json.JSONDecodeError:
|
96
99
|
valid_dict = {}
|
97
100
|
success = False
|
98
|
-
from rich import print
|
99
|
-
from rich.console import Console
|
100
|
-
from rich.syntax import Syntax
|
101
|
-
|
102
101
|
console = Console()
|
103
102
|
error_message = (
|
104
103
|
f"All repairs. failed. LLM Model given [red]{str(bad_json)}[/red]"
|
edsl/notebooks/Notebook.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
"""A Notebook is a utility class that allows you to easily share/pull ipynbs from Coop."""
|
2
2
|
|
3
3
|
import json
|
4
|
+
import nbformat
|
5
|
+
from nbconvert import HTMLExporter
|
4
6
|
from typing import Dict, List, Optional
|
5
|
-
|
6
|
-
|
7
|
+
from rich.table import Table
|
7
8
|
from edsl.Base import Base
|
8
9
|
from edsl.utilities.decorators import (
|
9
10
|
add_edsl_version,
|
@@ -33,8 +34,6 @@ class Notebook(Base):
|
|
33
34
|
If no path is provided, assume this code is run in a notebook and try to load the current notebook from file.
|
34
35
|
:param name: A name for the Notebook.
|
35
36
|
"""
|
36
|
-
import nbformat
|
37
|
-
|
38
37
|
# Load current notebook path as fallback (VS Code only)
|
39
38
|
path = path or globals().get("__vsc_ipynb_file__")
|
40
39
|
if data is not None:
|
@@ -57,37 +56,6 @@ class Notebook(Base):
|
|
57
56
|
|
58
57
|
self.name = name or self.default_name
|
59
58
|
|
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
|
-
|
91
59
|
def __eq__(self, other):
|
92
60
|
"""
|
93
61
|
Check if two Notebooks are equal.
|
@@ -135,9 +103,6 @@ class Notebook(Base):
|
|
135
103
|
"""
|
136
104
|
Return HTML representation of Notebook.
|
137
105
|
"""
|
138
|
-
from nbconvert import HTMLExporter
|
139
|
-
import nbformat
|
140
|
-
|
141
106
|
notebook = nbformat.from_dict(self.data)
|
142
107
|
html_exporter = HTMLExporter(template_name="basic")
|
143
108
|
(body, _) = html_exporter.from_notebook_node(notebook)
|
@@ -178,8 +143,6 @@ class Notebook(Base):
|
|
178
143
|
"""
|
179
144
|
Display a Notebook as a rich table.
|
180
145
|
"""
|
181
|
-
from rich.table import Table
|
182
|
-
|
183
146
|
table_data, column_names = self._table()
|
184
147
|
table = Table(title=f"{self.__class__.__name__} Attributes")
|
185
148
|
for column in column_names:
|
edsl/prompts/Prompt.py
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
+
"""Class for creating prompts to be used in a survey."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
from typing import Optional
|
3
5
|
from abc import ABC
|
4
6
|
from typing import Any, List
|
5
7
|
|
6
8
|
from rich.table import Table
|
7
|
-
from jinja2 import Template, Environment, meta, TemplateSyntaxError
|
8
|
-
|
9
|
-
|
10
|
-
class PreserveUndefined(Undefined):
|
11
|
-
def __str__(self):
|
12
|
-
return "{{ " + self._undefined_name + " }}"
|
13
|
-
|
9
|
+
from jinja2 import Template, Environment, meta, TemplateSyntaxError
|
14
10
|
|
15
11
|
from edsl.exceptions.prompts import TemplateRenderError
|
16
12
|
from edsl.prompts.prompt_config import (
|
@@ -39,10 +35,6 @@ class PromptBase(
|
|
39
35
|
|
40
36
|
return data_to_html(self.to_dict())
|
41
37
|
|
42
|
-
def __len__(self):
|
43
|
-
"""Return the length of the prompt text."""
|
44
|
-
return len(self.text)
|
45
|
-
|
46
38
|
@classmethod
|
47
39
|
def prompt_attributes(cls) -> List[str]:
|
48
40
|
"""Return the prompt class attributes."""
|
@@ -83,10 +75,10 @@ class PromptBase(
|
|
83
75
|
>>> p = Prompt("Hello, {{person}}")
|
84
76
|
>>> p2 = Prompt("How are you?")
|
85
77
|
>>> p + p2
|
86
|
-
Prompt(text
|
78
|
+
Prompt(text='Hello, {{person}}How are you?')
|
87
79
|
|
88
80
|
>>> p + "How are you?"
|
89
|
-
Prompt(text
|
81
|
+
Prompt(text='Hello, {{person}}How are you?')
|
90
82
|
"""
|
91
83
|
if isinstance(other_prompt, str):
|
92
84
|
return self.__class__(self.text + other_prompt)
|
@@ -122,7 +114,7 @@ class PromptBase(
|
|
122
114
|
Example:
|
123
115
|
>>> p = Prompt("Hello, {{person}}")
|
124
116
|
>>> p
|
125
|
-
Prompt(text
|
117
|
+
Prompt(text='Hello, {{person}}')
|
126
118
|
"""
|
127
119
|
return f'Prompt(text="""{self.text}""")'
|
128
120
|
|
@@ -145,7 +137,7 @@ class PromptBase(
|
|
145
137
|
:param template: The template to find the variables in.
|
146
138
|
|
147
139
|
"""
|
148
|
-
env = Environment(
|
140
|
+
env = Environment()
|
149
141
|
ast = env.parse(template)
|
150
142
|
return list(meta.find_undeclared_variables(ast))
|
151
143
|
|
@@ -194,16 +186,13 @@ class PromptBase(
|
|
194
186
|
|
195
187
|
>>> p = Prompt("Hello, {{person}}")
|
196
188
|
>>> p.render({"person": "John"})
|
197
|
-
|
189
|
+
'Hello, John'
|
198
190
|
|
199
191
|
>>> p.render({"person": "Mr. {{last_name}}", "last_name": "Horton"})
|
200
|
-
|
192
|
+
'Hello, Mr. Horton'
|
201
193
|
|
202
194
|
>>> p.render({"person": "Mr. {{last_name}}", "last_name": "Ho{{letter}}ton"}, max_nesting = 1)
|
203
|
-
|
204
|
-
|
205
|
-
>>> p.render({"person": "Mr. {{last_name}}"})
|
206
|
-
Prompt(text=\"""Hello, Mr. {{ last_name }}\""")
|
195
|
+
'Hello, Mr. Horton'
|
207
196
|
"""
|
208
197
|
new_text = self._render(
|
209
198
|
self.text, primary_replacement, **additional_replacements
|
@@ -227,13 +216,12 @@ class PromptBase(
|
|
227
216
|
>>> codebook = {"age": "Age"}
|
228
217
|
>>> p = Prompt("You are an agent named {{ name }}. {{ codebook['age']}}: {{ age }}")
|
229
218
|
>>> p.render({"name": "John", "age": 44}, codebook=codebook)
|
230
|
-
|
219
|
+
'You are an agent named John. Age: 44'
|
231
220
|
"""
|
232
|
-
env = Environment(undefined=PreserveUndefined)
|
233
221
|
try:
|
234
222
|
previous_text = None
|
235
223
|
for _ in range(MAX_NESTING):
|
236
|
-
rendered_text =
|
224
|
+
rendered_text = Template(text).render(
|
237
225
|
primary_replacement, **additional_replacements
|
238
226
|
)
|
239
227
|
if rendered_text == previous_text:
|
@@ -270,7 +258,7 @@ class PromptBase(
|
|
270
258
|
>>> p = Prompt("Hello, {{person}}")
|
271
259
|
>>> p2 = Prompt.from_dict(p.to_dict())
|
272
260
|
>>> p2
|
273
|
-
Prompt(text
|
261
|
+
Prompt(text='Hello, {{person}}')
|
274
262
|
|
275
263
|
"""
|
276
264
|
class_name = data["class_name"]
|
@@ -302,12 +290,6 @@ class Prompt(PromptBase):
|
|
302
290
|
component_type = ComponentTypes.GENERIC
|
303
291
|
|
304
292
|
|
305
|
-
if __name__ == "__main__":
|
306
|
-
print("Running doctests...")
|
307
|
-
import doctest
|
308
|
-
|
309
|
-
doctest.testmod()
|
310
|
-
|
311
293
|
from edsl.prompts.library.question_multiple_choice import *
|
312
294
|
from edsl.prompts.library.agent_instructions import *
|
313
295
|
from edsl.prompts.library.agent_persona import *
|
@@ -320,3 +302,9 @@ from edsl.prompts.library.question_numerical import *
|
|
320
302
|
from edsl.prompts.library.question_rank import *
|
321
303
|
from edsl.prompts.library.question_extract import *
|
322
304
|
from edsl.prompts.library.question_list import *
|
305
|
+
|
306
|
+
|
307
|
+
if __name__ == "__main__":
|
308
|
+
import doctest
|
309
|
+
|
310
|
+
doctest.testmod()
|
edsl/questions/QuestionBase.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
"""This module contains the Question class, which is the base class for all questions in EDSL."""
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
|
-
import time
|
5
4
|
from abc import ABC, abstractmethod
|
5
|
+
from rich.table import Table
|
6
6
|
from typing import Any, Type, Optional, List, Callable
|
7
7
|
import copy
|
8
8
|
|
@@ -12,7 +12,7 @@ from edsl.exceptions import (
|
|
12
12
|
)
|
13
13
|
from edsl.questions.descriptors import QuestionNameDescriptor, QuestionTextDescriptor
|
14
14
|
|
15
|
-
|
15
|
+
from edsl.prompts.registry import get_classes as prompt_lookup
|
16
16
|
from edsl.questions.AnswerValidatorMixin import AnswerValidatorMixin
|
17
17
|
from edsl.questions.RegisterQuestionsMeta import RegisterQuestionsMeta
|
18
18
|
from edsl.Base import PersistenceMixin, RichPrintingMixin
|
@@ -124,8 +124,6 @@ class QuestionBase(
|
|
124
124
|
:param model: The language model to use. If None, assumes does not matter.
|
125
125
|
|
126
126
|
"""
|
127
|
-
from edsl.prompts.registry import get_classes as prompt_lookup
|
128
|
-
|
129
127
|
applicable_prompts = prompt_lookup(
|
130
128
|
component_type="question_instructions",
|
131
129
|
question_type=cls.question_type,
|
@@ -175,16 +173,15 @@ class QuestionBase(
|
|
175
173
|
def add_model_instructions(
|
176
174
|
self, *, instructions: str, model: Optional[str] = None
|
177
175
|
) -> None:
|
178
|
-
"""Add model-specific instructions for the question
|
176
|
+
"""Add model-specific instructions for the question.
|
179
177
|
|
180
178
|
:param instructions: The instructions to add. This is typically a jinja2 template.
|
181
179
|
:param model: The language model for this instruction.
|
182
180
|
|
183
181
|
>>> from edsl.questions import QuestionFreeText
|
184
182
|
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
|
185
|
-
>>> q.add_model_instructions(instructions = "
|
186
|
-
|
187
|
-
Prompt(text=\"""{{question_text}}. Answer in valid JSON like so {'answer': 'comment: <>}\""")
|
183
|
+
>>> q.add_model_instructions(instructions = "Answer in valid JSON like so {'answer': 'comment: <>}", model = "gpt3")
|
184
|
+
|
188
185
|
"""
|
189
186
|
from edsl import Model
|
190
187
|
|
@@ -204,13 +201,6 @@ class QuestionBase(
|
|
204
201
|
"""Get the mathcing question-answering instructions for the question.
|
205
202
|
|
206
203
|
:param model: The language model to use.
|
207
|
-
|
208
|
-
>>> from edsl import QuestionFreeText
|
209
|
-
>>> QuestionFreeText.example().get_instructions()
|
210
|
-
Prompt(text=\"""You are being asked the following question: {{question_text}}
|
211
|
-
Return a valid JSON formatted like this:
|
212
|
-
{"answer": "<put free text answer here>"}
|
213
|
-
\""")
|
214
204
|
"""
|
215
205
|
from edsl.prompts.Prompt import Prompt
|
216
206
|
|
@@ -303,16 +293,7 @@ class QuestionBase(
|
|
303
293
|
print_json(json.dumps(self.to_dict()))
|
304
294
|
|
305
295
|
def __call__(self, just_answer=True, model=None, agent=None, **kwargs):
|
306
|
-
"""Call the question.
|
307
|
-
|
308
|
-
>>> from edsl.language_models import LanguageModel
|
309
|
-
>>> m = LanguageModel.example(canned_response = "Yo, what's up?", test_model = True)
|
310
|
-
>>> from edsl import QuestionFreeText
|
311
|
-
>>> q = QuestionFreeText(question_name = "color", question_text = "What is your favorite color?")
|
312
|
-
>>> q(model = m)
|
313
|
-
"Yo, what's up?"
|
314
|
-
|
315
|
-
"""
|
296
|
+
"""Call the question."""
|
316
297
|
survey = self.to_survey()
|
317
298
|
results = survey(model=model, agent=agent, **kwargs)
|
318
299
|
if just_answer:
|
@@ -323,6 +304,7 @@ class QuestionBase(
|
|
323
304
|
async def run_async(self, just_answer=True, model=None, agent=None, **kwargs):
|
324
305
|
"""Call the question."""
|
325
306
|
survey = self.to_survey()
|
307
|
+
## asyncio.run(survey.async_call());
|
326
308
|
results = await survey.run_async(model=model, agent=agent, **kwargs)
|
327
309
|
if just_answer:
|
328
310
|
return results.select(f"answer.{self.question_name}").first()
|
@@ -401,34 +383,29 @@ class QuestionBase(
|
|
401
383
|
s = Survey([self, other])
|
402
384
|
return s
|
403
385
|
|
404
|
-
def to_survey(self)
|
386
|
+
def to_survey(self):
|
405
387
|
"""Turn a single question into a survey."""
|
406
388
|
from edsl.surveys.Survey import Survey
|
407
389
|
|
408
390
|
s = Survey([self])
|
409
391
|
return s
|
410
392
|
|
411
|
-
def run(self, *args, **kwargs)
|
393
|
+
def run(self, *args, **kwargs):
|
412
394
|
"""Turn a single question into a survey and run it."""
|
413
395
|
from edsl.surveys.Survey import Survey
|
414
396
|
|
415
397
|
s = self.to_survey()
|
416
398
|
return s.run(*args, **kwargs)
|
417
399
|
|
418
|
-
def by(self, *args)
|
419
|
-
"""Turn a single question into a survey and
|
400
|
+
def by(self, *args):
|
401
|
+
"""Turn a single question into a survey and run it."""
|
420
402
|
from edsl.surveys.Survey import Survey
|
421
403
|
|
422
404
|
s = Survey([self])
|
423
405
|
return s.by(*args)
|
424
406
|
|
425
|
-
def human_readable(self)
|
426
|
-
"""Print the question in a human readable format.
|
427
|
-
|
428
|
-
>>> from edsl.questions import QuestionFreeText
|
429
|
-
>>> QuestionFreeText.example().human_readable()
|
430
|
-
'Question Type: free_text\\nQuestion: How are you?'
|
431
|
-
"""
|
407
|
+
def human_readable(self):
|
408
|
+
"""Print the question in a human readable format."""
|
432
409
|
lines = []
|
433
410
|
lines.append(f"Question Type: {self.question_type}")
|
434
411
|
lines.append(f"Question: {self.question_text}")
|
@@ -498,8 +475,6 @@ class QuestionBase(
|
|
498
475
|
|
499
476
|
def rich_print(self):
|
500
477
|
"""Print the question in a rich format."""
|
501
|
-
from rich.table import Table
|
502
|
-
|
503
478
|
table = Table(show_header=True, header_style="bold magenta")
|
504
479
|
table.add_column("Question Name", style="dim")
|
505
480
|
table.add_column("Question Type")
|
edsl/questions/QuestionBudget.py
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import random
|
3
|
+
import textwrap
|
3
4
|
from typing import Any, Optional, Union
|
4
5
|
from edsl.questions.QuestionBase import QuestionBase
|
5
6
|
from edsl.questions.descriptors import IntegerDescriptor, QuestionOptionsDescriptor
|
7
|
+
from edsl.scenarios import Scenario
|
8
|
+
from edsl.utilities import random_string
|
6
9
|
|
7
10
|
|
8
11
|
class QuestionBudget(QuestionBase):
|
@@ -43,7 +46,7 @@ class QuestionBudget(QuestionBase):
|
|
43
46
|
return answer
|
44
47
|
|
45
48
|
def _translate_answer_code_to_answer(
|
46
|
-
self, answer_codes: dict[str, int], scenario:
|
49
|
+
self, answer_codes: dict[str, int], scenario: Scenario = None
|
47
50
|
):
|
48
51
|
"""
|
49
52
|
Translate the answer codes to the actual answers.
|
@@ -60,8 +63,6 @@ class QuestionBudget(QuestionBase):
|
|
60
63
|
|
61
64
|
def _simulate_answer(self, human_readable=True):
|
62
65
|
"""Simulate a valid answer for debugging purposes (what the validator expects)."""
|
63
|
-
from edsl.utilities.utilities import random_string
|
64
|
-
|
65
66
|
if human_readable:
|
66
67
|
keys = self.question_options
|
67
68
|
else:
|
@@ -162,8 +163,8 @@ def main():
|
|
162
163
|
|
163
164
|
|
164
165
|
if __name__ == "__main__":
|
165
|
-
|
166
|
-
|
166
|
+
q = QuestionBudget.example()
|
167
|
+
results = q.run()
|
167
168
|
|
168
169
|
import doctest
|
169
170
|
|
@@ -9,6 +9,8 @@ from edsl.questions.descriptors import (
|
|
9
9
|
IntegerDescriptor,
|
10
10
|
QuestionOptionsDescriptor,
|
11
11
|
)
|
12
|
+
from edsl.scenarios import Scenario
|
13
|
+
from edsl.utilities import random_string
|
12
14
|
|
13
15
|
|
14
16
|
class QuestionCheckBox(QuestionBase):
|
@@ -53,17 +55,13 @@ class QuestionCheckBox(QuestionBase):
|
|
53
55
|
self._validate_answer_checkbox(answer)
|
54
56
|
return answer
|
55
57
|
|
56
|
-
def _translate_answer_code_to_answer(
|
57
|
-
self, answer_codes, scenario: "Scenario" = None
|
58
|
-
):
|
58
|
+
def _translate_answer_code_to_answer(self, answer_codes, scenario: Scenario = None):
|
59
59
|
"""
|
60
60
|
Translate the answer code to the actual answer.
|
61
61
|
|
62
62
|
For example, for question options ["a", "b", "c"],the answer codes are 0, 1, and 2.
|
63
63
|
The LLM will respond with [0,1] and this code will translate it to ["a","b"].
|
64
64
|
"""
|
65
|
-
from edsl.scenarios.Scenario import Scenario
|
66
|
-
|
67
65
|
scenario = scenario or Scenario()
|
68
66
|
translated_options = [
|
69
67
|
Template(option).render(scenario) for option in self.question_options
|
@@ -75,8 +73,6 @@ class QuestionCheckBox(QuestionBase):
|
|
75
73
|
|
76
74
|
def _simulate_answer(self, human_readable=True) -> dict[str, Union[int, str]]:
|
77
75
|
"""Simulate a valid answer for debugging purposes."""
|
78
|
-
from edsl.utilities.utilities import random_string
|
79
|
-
|
80
76
|
min_selections = self.min_selections or 1
|
81
77
|
max_selections = self.max_selections or len(self.question_options)
|
82
78
|
num_selections = random.randint(min_selections, max_selections)
|