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
@@ -1,331 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import time
|
4
|
-
from dataclasses import dataclass, asdict
|
5
|
-
|
6
|
-
from typing import List, DefaultDict, Optional, Type, Literal
|
7
|
-
from collections import UserDict, defaultdict
|
8
|
-
|
9
|
-
from rich.text import Text
|
10
|
-
from rich.box import SIMPLE
|
11
|
-
from rich.table import Table
|
12
|
-
from rich.live import Live
|
13
|
-
from rich.panel import Panel
|
14
|
-
from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn
|
15
|
-
from rich.layout import Layout
|
16
|
-
from rich.console import Group
|
17
|
-
from rich import box
|
18
|
-
|
19
|
-
from edsl.jobs.interviews.InterviewStatusDictionary import InterviewStatusDictionary
|
20
|
-
from edsl.jobs.tokens.InterviewTokenUsage import InterviewTokenUsage
|
21
|
-
from edsl.jobs.tokens.TokenUsage import TokenUsage
|
22
|
-
from edsl.enums import get_token_pricing
|
23
|
-
from edsl.jobs.tasks.task_status_enum import TaskStatus
|
24
|
-
|
25
|
-
InterviewTokenUsageMapping = DefaultDict[str, InterviewTokenUsage]
|
26
|
-
|
27
|
-
from edsl.jobs.interviews.InterviewStatistic import InterviewStatistic
|
28
|
-
from edsl.jobs.interviews.InterviewStatisticsCollection import (
|
29
|
-
InterviewStatisticsCollection,
|
30
|
-
)
|
31
|
-
from edsl.jobs.tokens.InterviewTokenUsage import InterviewTokenUsage
|
32
|
-
|
33
|
-
|
34
|
-
@dataclass
|
35
|
-
class ModelInfo:
|
36
|
-
model_name: str
|
37
|
-
TPM_limit_k: float
|
38
|
-
RPM_limit_k: float
|
39
|
-
num_tasks_waiting: int
|
40
|
-
token_usage_info: dict
|
41
|
-
|
42
|
-
|
43
|
-
@dataclass
|
44
|
-
class ModelTokenUsageStats:
|
45
|
-
token_usage_type: str
|
46
|
-
details: List[dict]
|
47
|
-
cost: str
|
48
|
-
|
49
|
-
|
50
|
-
class Stats:
|
51
|
-
def elapsed_time(self):
|
52
|
-
InterviewStatistic("elapsed_time", value=elapsed_time, digits=1, units="sec.")
|
53
|
-
|
54
|
-
|
55
|
-
class JobsRunnerStatus:
|
56
|
-
def __init__(
|
57
|
-
self, jobs_runner: "JobsRunnerAsyncio", n: int, refresh_rate: float = 0.25
|
58
|
-
):
|
59
|
-
self.jobs_runner = jobs_runner
|
60
|
-
self.start_time = time.time()
|
61
|
-
self.completed_interviews = []
|
62
|
-
self.refresh_rate = refresh_rate
|
63
|
-
self.statistics = [
|
64
|
-
"elapsed_time",
|
65
|
-
"total_interviews_requested",
|
66
|
-
"completed_interviews",
|
67
|
-
# "percent_complete",
|
68
|
-
"average_time_per_interview",
|
69
|
-
# "task_remaining",
|
70
|
-
"estimated_time_remaining",
|
71
|
-
"exceptions",
|
72
|
-
"unfixed_exceptions",
|
73
|
-
"throughput",
|
74
|
-
]
|
75
|
-
self.num_total_interviews = n * len(self.jobs_runner.interviews)
|
76
|
-
|
77
|
-
self.distinct_models = list(
|
78
|
-
set(i.model.model for i in self.jobs_runner.interviews)
|
79
|
-
)
|
80
|
-
|
81
|
-
self.completed_interview_by_model = defaultdict(list)
|
82
|
-
|
83
|
-
def add_completed_interview(self, result):
|
84
|
-
self.completed_interviews.append(result.interview_hash)
|
85
|
-
|
86
|
-
relevant_model = result.model.model
|
87
|
-
self.completed_interview_by_model[relevant_model].append(result.interview_hash)
|
88
|
-
|
89
|
-
def _compute_statistic(self, stat_name: str):
|
90
|
-
completed_tasks = self.completed_interviews
|
91
|
-
elapsed_time = time.time() - self.start_time
|
92
|
-
interviews = self.jobs_runner.total_interviews
|
93
|
-
|
94
|
-
stat_definitions = {
|
95
|
-
"elapsed_time": lambda: InterviewStatistic(
|
96
|
-
"elapsed_time", value=elapsed_time, digits=1, units="sec."
|
97
|
-
),
|
98
|
-
"total_interviews_requested": lambda: InterviewStatistic(
|
99
|
-
"total_interviews_requested", value=len(interviews), units=""
|
100
|
-
),
|
101
|
-
"completed_interviews": lambda: InterviewStatistic(
|
102
|
-
"completed_interviews", value=len(completed_tasks), units=""
|
103
|
-
),
|
104
|
-
"percent_complete": lambda: InterviewStatistic(
|
105
|
-
"percent_complete",
|
106
|
-
value=(
|
107
|
-
len(completed_tasks) / len(interviews) * 100
|
108
|
-
if len(interviews) > 0
|
109
|
-
else 0
|
110
|
-
),
|
111
|
-
digits=1,
|
112
|
-
units="%",
|
113
|
-
),
|
114
|
-
"average_time_per_interview": lambda: InterviewStatistic(
|
115
|
-
"average_time_per_interview",
|
116
|
-
value=elapsed_time / len(completed_tasks) if completed_tasks else 0,
|
117
|
-
digits=2,
|
118
|
-
units="sec.",
|
119
|
-
),
|
120
|
-
"task_remaining": lambda: InterviewStatistic(
|
121
|
-
"task_remaining", value=len(interviews) - len(completed_tasks), units=""
|
122
|
-
),
|
123
|
-
"estimated_time_remaining": lambda: InterviewStatistic(
|
124
|
-
"estimated_time_remaining",
|
125
|
-
value=(
|
126
|
-
(len(interviews) - len(completed_tasks))
|
127
|
-
* (elapsed_time / len(completed_tasks))
|
128
|
-
if len(completed_tasks) > 0
|
129
|
-
else 0
|
130
|
-
),
|
131
|
-
digits=1,
|
132
|
-
units="sec.",
|
133
|
-
),
|
134
|
-
"exceptions": lambda: InterviewStatistic(
|
135
|
-
"exceptions",
|
136
|
-
value=sum(len(i.exceptions) for i in interviews),
|
137
|
-
units="",
|
138
|
-
),
|
139
|
-
"unfixed_exceptions": lambda: InterviewStatistic(
|
140
|
-
"unfixed_exceptions",
|
141
|
-
value=sum(i.exceptions.num_unfixed() for i in interviews),
|
142
|
-
units="",
|
143
|
-
),
|
144
|
-
"throughput": lambda: InterviewStatistic(
|
145
|
-
"throughput",
|
146
|
-
value=len(completed_tasks) / elapsed_time if elapsed_time > 0 else 0,
|
147
|
-
digits=2,
|
148
|
-
units="interviews/sec.",
|
149
|
-
),
|
150
|
-
}
|
151
|
-
return stat_definitions[stat_name]()
|
152
|
-
|
153
|
-
def create_progress_bar(self):
|
154
|
-
return Progress(
|
155
|
-
TextColumn("[progress.description]{task.description}"),
|
156
|
-
BarColumn(),
|
157
|
-
TaskProgressColumn(),
|
158
|
-
TextColumn("{task.completed}/{task.total}"),
|
159
|
-
)
|
160
|
-
|
161
|
-
def generate_model_queues_table(self):
|
162
|
-
table = Table(show_header=False, box=box.SIMPLE)
|
163
|
-
table.add_column("Info", style="cyan")
|
164
|
-
table.add_column("Value", style="magenta")
|
165
|
-
# table.add_row("Bucket collection", str(self.jobs_runner.bucket_collection))
|
166
|
-
for model, bucket in self.jobs_runner.bucket_collection.items():
|
167
|
-
table.add_row(Text(model.model, style="bold blue"), "")
|
168
|
-
bucket_types = ["requests_bucket", "tokens_bucket"]
|
169
|
-
for bucket_type in bucket_types:
|
170
|
-
table.add_row(Text(" " + bucket_type, style="green"), "")
|
171
|
-
# table.add_row(
|
172
|
-
# f" Current level (capacity = {round(getattr(bucket, bucket_type).capacity, 3)})",
|
173
|
-
# str(round(getattr(bucket, bucket_type).tokens, 3)),
|
174
|
-
# )
|
175
|
-
num_requests = getattr(bucket, bucket_type).num_requests
|
176
|
-
num_released = getattr(bucket, bucket_type).num_released
|
177
|
-
tokens_returned = getattr(bucket, bucket_type).tokens_returned
|
178
|
-
# table.add_row(
|
179
|
-
# f" Requested",
|
180
|
-
# str(num_requests),
|
181
|
-
# )
|
182
|
-
# table.add_row(
|
183
|
-
# f" Completed",
|
184
|
-
# str(num_released),
|
185
|
-
# )
|
186
|
-
table.add_row(
|
187
|
-
" Completed vs. Requested", f"{num_released} vs. {num_requests}"
|
188
|
-
)
|
189
|
-
table.add_row(
|
190
|
-
" Added tokens (from cache)",
|
191
|
-
str(tokens_returned),
|
192
|
-
)
|
193
|
-
if bucket_type == "tokens_bucket":
|
194
|
-
rate_name = "TPM"
|
195
|
-
else:
|
196
|
-
rate_name = "RPM"
|
197
|
-
target_rate = round(getattr(bucket, bucket_type).target_rate, 1)
|
198
|
-
table.add_row(
|
199
|
-
f" Empirical {rate_name} (target = {target_rate})",
|
200
|
-
str(round(getattr(bucket, bucket_type).get_throughput(), 0)),
|
201
|
-
)
|
202
|
-
|
203
|
-
return table
|
204
|
-
|
205
|
-
def generate_layout(self):
|
206
|
-
progress = self.create_progress_bar()
|
207
|
-
task_ids = []
|
208
|
-
for model in self.distinct_models:
|
209
|
-
task_id = progress.add_task(
|
210
|
-
f"[cyan]{model}...",
|
211
|
-
total=int(self.num_total_interviews / len(self.distinct_models)),
|
212
|
-
)
|
213
|
-
task_ids.append((model, task_id))
|
214
|
-
|
215
|
-
progress_height = min(5, 2 + len(self.distinct_models))
|
216
|
-
layout = Layout()
|
217
|
-
|
218
|
-
# Create the top row with only the progress panel
|
219
|
-
layout.split_column(
|
220
|
-
Layout(
|
221
|
-
Panel(
|
222
|
-
progress,
|
223
|
-
title="Interview Progress",
|
224
|
-
border_style="cyan",
|
225
|
-
box=box.ROUNDED,
|
226
|
-
),
|
227
|
-
name="progress",
|
228
|
-
size=progress_height, # Adjusted size
|
229
|
-
),
|
230
|
-
Layout(name="bottom_row"), # Adjusted size
|
231
|
-
)
|
232
|
-
|
233
|
-
# Split the bottom row into two columns for metrics and model queues
|
234
|
-
layout["bottom_row"].split_row(
|
235
|
-
Layout(
|
236
|
-
Panel(
|
237
|
-
self.generate_metrics_table(),
|
238
|
-
title="Metrics",
|
239
|
-
border_style="magenta",
|
240
|
-
box=box.ROUNDED,
|
241
|
-
),
|
242
|
-
name="metrics",
|
243
|
-
),
|
244
|
-
Layout(
|
245
|
-
Panel(
|
246
|
-
self.generate_model_queues_table(),
|
247
|
-
title="Model Queues",
|
248
|
-
border_style="yellow",
|
249
|
-
box=box.ROUNDED,
|
250
|
-
),
|
251
|
-
name="model_queues",
|
252
|
-
),
|
253
|
-
)
|
254
|
-
|
255
|
-
return layout, progress, task_ids
|
256
|
-
|
257
|
-
def generate_metrics_table(self):
|
258
|
-
table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE)
|
259
|
-
table.add_column("Metric", style="cyan", no_wrap=True)
|
260
|
-
table.add_column("Value", justify="right")
|
261
|
-
|
262
|
-
for stat_name in self.statistics:
|
263
|
-
pretty_name, value = list(self._compute_statistic(stat_name).items())[0]
|
264
|
-
# breakpoint()
|
265
|
-
table.add_row(pretty_name, value)
|
266
|
-
return table
|
267
|
-
|
268
|
-
def update_progress(self):
|
269
|
-
layout, progress, task_ids = self.generate_layout()
|
270
|
-
|
271
|
-
with Live(
|
272
|
-
layout, refresh_per_second=int(1 / self.refresh_rate), transient=True
|
273
|
-
) as live:
|
274
|
-
while len(self.completed_interviews) < len(
|
275
|
-
self.jobs_runner.total_interviews
|
276
|
-
):
|
277
|
-
completed_tasks = len(self.completed_interviews)
|
278
|
-
total_tasks = len(self.jobs_runner.total_interviews)
|
279
|
-
|
280
|
-
for model, task_id in task_ids:
|
281
|
-
completed_tasks = len(self.completed_interview_by_model[model])
|
282
|
-
progress.update(
|
283
|
-
task_id,
|
284
|
-
completed=completed_tasks,
|
285
|
-
description=f"[cyan]Conducting interviews for {model}...",
|
286
|
-
)
|
287
|
-
|
288
|
-
layout["metrics"].update(
|
289
|
-
Panel(
|
290
|
-
self.generate_metrics_table(),
|
291
|
-
title="Metrics",
|
292
|
-
border_style="magenta",
|
293
|
-
box=box.ROUNDED,
|
294
|
-
)
|
295
|
-
)
|
296
|
-
layout["model_queues"].update(
|
297
|
-
Panel(
|
298
|
-
self.generate_model_queues_table(),
|
299
|
-
title="Final Model Queues",
|
300
|
-
border_style="yellow",
|
301
|
-
box=box.ROUNDED,
|
302
|
-
)
|
303
|
-
)
|
304
|
-
|
305
|
-
time.sleep(self.refresh_rate)
|
306
|
-
|
307
|
-
# Final update
|
308
|
-
for model, task_id in task_ids:
|
309
|
-
completed_tasks = len(self.completed_interview_by_model[model])
|
310
|
-
progress.update(
|
311
|
-
task_id,
|
312
|
-
completed=completed_tasks,
|
313
|
-
description=f"[cyan]Conducting interviews for {model}...",
|
314
|
-
)
|
315
|
-
|
316
|
-
layout["metrics"].update(
|
317
|
-
Panel(
|
318
|
-
self.generate_metrics_table(),
|
319
|
-
title="Final Metrics",
|
320
|
-
border_style="magenta",
|
321
|
-
box=box.ROUNDED,
|
322
|
-
)
|
323
|
-
)
|
324
|
-
live.update(layout)
|
325
|
-
time.sleep(1) # Show final state for 1 second
|
326
|
-
|
327
|
-
|
328
|
-
if __name__ == "__main__":
|
329
|
-
import doctest
|
330
|
-
|
331
|
-
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/questions/Quick.py
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
from edsl import (
|
2
|
-
QuestionFreeText,
|
3
|
-
QuestionMultipleChoice,
|
4
|
-
Survey,
|
5
|
-
QuestionList,
|
6
|
-
Question,
|
7
|
-
)
|
8
|
-
|
9
|
-
|
10
|
-
def Quick(question_text):
|
11
|
-
q_type = QuestionMultipleChoice(
|
12
|
-
question_text=f"A researcher is asking a language model this: {question_text}. What is the most appropriate type of question to ask?",
|
13
|
-
question_name="potential_question_type",
|
14
|
-
question_options=["multiple_choice", "list", "free_text"],
|
15
|
-
)
|
16
|
-
|
17
|
-
q_name = QuestionFreeText(
|
18
|
-
question_text=f"A researcher is asking a language model this: {question_text}. What is a good name for this question that's a valid python identifier? Just return the proposed identifer",
|
19
|
-
question_name="potential_question_name",
|
20
|
-
)
|
21
|
-
|
22
|
-
q_options = QuestionList(
|
23
|
-
question_text=f"A research is asking this question: { question_text }. What are the possible options for this question?",
|
24
|
-
question_name="potential_question_options",
|
25
|
-
)
|
26
|
-
|
27
|
-
survey = Survey([q_type, q_name, q_options]).add_skip_rule(
|
28
|
-
q_options, "{{ potential_question_type }} != 'multiple_choice'"
|
29
|
-
)
|
30
|
-
return survey
|
31
|
-
# results = survey.run()
|
32
|
-
# question_type = results.select("potential_question_type").first()
|
33
|
-
# question_options = results.select("potential_question_options").first()
|
34
|
-
# question_name = results.select("potential_question_name").first()
|
35
|
-
# print("Question Type: ", question_type)
|
36
|
-
# print("Question Name: ", question_name)
|
37
|
-
# print("Question Options: ", question_options)
|
38
|
-
# if question_options == None:
|
39
|
-
# return Question(question_type, question_name = question_name)
|
40
|
-
# else:
|
41
|
-
# return Question(question_type, question_name = question_name, question_options = question_options)
|
File without changes
|
@@ -1,7 +0,0 @@
|
|
1
|
-
Return only a comma-separated list the values in the same order as the options, with 0s included, on one line, in square braces.
|
2
|
-
|
3
|
-
Example: if there are 4 options, the response should be "[25,25,25,25]" to allocate 25 to each option.
|
4
|
-
|
5
|
-
{% if include_comment %}
|
6
|
-
After the answer, you can put a comment explaining your choice on the next line.
|
7
|
-
{% endif %}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|