edsl 0.1.37.dev5__py3-none-any.whl → 0.1.38__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 +63 -34
- edsl/BaseDiff.py +7 -7
- edsl/__init__.py +2 -1
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +23 -11
- edsl/agents/AgentList.py +86 -23
- edsl/agents/Invigilator.py +18 -7
- edsl/agents/InvigilatorBase.py +0 -19
- edsl/agents/PromptConstructor.py +5 -4
- edsl/auto/SurveyCreatorPipeline.py +1 -1
- edsl/auto/utilities.py +1 -1
- edsl/base/Base.py +3 -13
- edsl/config.py +8 -0
- edsl/coop/coop.py +89 -19
- edsl/data/Cache.py +45 -17
- edsl/data/CacheEntry.py +8 -3
- edsl/data/RemoteCacheSync.py +0 -19
- edsl/enums.py +2 -0
- edsl/exceptions/agents.py +4 -0
- edsl/exceptions/cache.py +5 -0
- edsl/inference_services/GoogleService.py +7 -15
- edsl/inference_services/PerplexityService.py +163 -0
- edsl/inference_services/registry.py +2 -0
- edsl/jobs/Jobs.py +110 -559
- edsl/jobs/JobsChecks.py +147 -0
- edsl/jobs/JobsPrompts.py +268 -0
- edsl/jobs/JobsRemoteInferenceHandler.py +239 -0
- edsl/jobs/buckets/TokenBucket.py +3 -0
- edsl/jobs/interviews/Interview.py +7 -7
- edsl/jobs/runners/JobsRunnerAsyncio.py +156 -28
- edsl/jobs/runners/JobsRunnerStatus.py +194 -196
- edsl/jobs/tasks/TaskHistory.py +27 -19
- edsl/language_models/LanguageModel.py +52 -90
- edsl/language_models/ModelList.py +67 -14
- edsl/language_models/registry.py +57 -4
- edsl/notebooks/Notebook.py +7 -8
- edsl/prompts/Prompt.py +8 -3
- edsl/questions/QuestionBase.py +38 -30
- edsl/questions/QuestionBaseGenMixin.py +1 -1
- edsl/questions/QuestionBasePromptsMixin.py +0 -17
- edsl/questions/QuestionExtract.py +3 -4
- edsl/questions/QuestionFunctional.py +10 -3
- edsl/questions/derived/QuestionTopK.py +2 -0
- edsl/questions/question_registry.py +36 -6
- edsl/results/CSSParameterizer.py +108 -0
- edsl/results/Dataset.py +146 -15
- edsl/results/DatasetExportMixin.py +231 -217
- edsl/results/DatasetTree.py +134 -4
- edsl/results/Result.py +31 -16
- edsl/results/Results.py +159 -65
- edsl/results/TableDisplay.py +198 -0
- edsl/results/table_display.css +78 -0
- edsl/scenarios/FileStore.py +187 -13
- edsl/scenarios/Scenario.py +73 -18
- edsl/scenarios/ScenarioJoin.py +127 -0
- edsl/scenarios/ScenarioList.py +251 -76
- edsl/surveys/MemoryPlan.py +1 -1
- edsl/surveys/Rule.py +1 -5
- edsl/surveys/RuleCollection.py +1 -1
- edsl/surveys/Survey.py +25 -19
- edsl/surveys/SurveyFlowVisualizationMixin.py +67 -9
- edsl/surveys/instructions/ChangeInstruction.py +9 -7
- edsl/surveys/instructions/Instruction.py +21 -7
- edsl/templates/error_reporting/interview_details.html +3 -3
- edsl/templates/error_reporting/interviews.html +18 -9
- edsl/{conjure → utilities}/naming_utilities.py +1 -1
- edsl/utilities/utilities.py +15 -0
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/METADATA +2 -1
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/RECORD +71 -77
- edsl/conjure/AgentConstructionMixin.py +0 -160
- edsl/conjure/Conjure.py +0 -62
- edsl/conjure/InputData.py +0 -659
- edsl/conjure/InputDataCSV.py +0 -48
- edsl/conjure/InputDataMixinQuestionStats.py +0 -182
- edsl/conjure/InputDataPyRead.py +0 -91
- edsl/conjure/InputDataSPSS.py +0 -8
- edsl/conjure/InputDataStata.py +0 -8
- edsl/conjure/QuestionOptionMixin.py +0 -76
- edsl/conjure/QuestionTypeMixin.py +0 -23
- edsl/conjure/RawQuestion.py +0 -65
- edsl/conjure/SurveyResponses.py +0 -7
- edsl/conjure/__init__.py +0 -9
- edsl/conjure/examples/placeholder.txt +0 -0
- edsl/conjure/utilities.py +0 -201
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/LICENSE +0 -0
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/WHEEL +0 -0
@@ -1,33 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import os
|
3
4
|
import time
|
4
|
-
|
5
|
-
|
6
|
-
from
|
7
|
-
from
|
8
|
-
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
|
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
|
5
|
+
import requests
|
6
|
+
import warnings
|
7
|
+
from abc import ABC, abstractmethod
|
8
|
+
from dataclasses import dataclass
|
9
|
+
|
10
|
+
from typing import Any, List, DefaultDict, Optional, Dict
|
11
|
+
from collections import defaultdict
|
12
|
+
from uuid import UUID
|
13
|
+
|
20
14
|
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
15
|
|
25
16
|
InterviewTokenUsageMapping = DefaultDict[str, InterviewTokenUsage]
|
26
17
|
|
27
18
|
from edsl.jobs.interviews.InterviewStatistic import InterviewStatistic
|
28
|
-
from edsl.jobs.interviews.InterviewStatisticsCollection import (
|
29
|
-
InterviewStatisticsCollection,
|
30
|
-
)
|
31
19
|
from edsl.jobs.tokens.InterviewTokenUsage import InterviewTokenUsage
|
32
20
|
|
33
21
|
|
@@ -47,16 +35,23 @@ class ModelTokenUsageStats:
|
|
47
35
|
cost: str
|
48
36
|
|
49
37
|
|
50
|
-
class
|
51
|
-
def elapsed_time(self):
|
52
|
-
InterviewStatistic("elapsed_time", value=elapsed_time, digits=1, units="sec.")
|
53
|
-
|
54
|
-
|
55
|
-
class JobsRunnerStatus:
|
38
|
+
class JobsRunnerStatusBase(ABC):
|
56
39
|
def __init__(
|
57
|
-
self,
|
40
|
+
self,
|
41
|
+
jobs_runner: "JobsRunnerAsyncio",
|
42
|
+
n: int,
|
43
|
+
refresh_rate: float = 1,
|
44
|
+
endpoint_url: Optional[str] = "http://localhost:8000",
|
45
|
+
job_uuid: Optional[UUID] = None,
|
46
|
+
api_key: str = None,
|
58
47
|
):
|
59
48
|
self.jobs_runner = jobs_runner
|
49
|
+
|
50
|
+
# The uuid of the job on Coop
|
51
|
+
self.job_uuid = job_uuid
|
52
|
+
|
53
|
+
self.base_url = f"{endpoint_url}"
|
54
|
+
|
60
55
|
self.start_time = time.time()
|
61
56
|
self.completed_interviews = []
|
62
57
|
self.refresh_rate = refresh_rate
|
@@ -80,6 +75,99 @@ class JobsRunnerStatus:
|
|
80
75
|
|
81
76
|
self.completed_interview_by_model = defaultdict(list)
|
82
77
|
|
78
|
+
self.api_key = api_key or os.getenv("EXPECTED_PARROT_API_KEY")
|
79
|
+
|
80
|
+
@abstractmethod
|
81
|
+
def has_ep_api_key(self):
|
82
|
+
"""
|
83
|
+
Checks if the user has an Expected Parrot API key.
|
84
|
+
"""
|
85
|
+
pass
|
86
|
+
|
87
|
+
def get_status_dict(self) -> Dict[str, Any]:
|
88
|
+
"""
|
89
|
+
Converts current status into a JSON-serializable dictionary.
|
90
|
+
"""
|
91
|
+
# Get all statistics
|
92
|
+
stats = {}
|
93
|
+
for stat_name in self.statistics:
|
94
|
+
stat = self._compute_statistic(stat_name)
|
95
|
+
name, value = list(stat.items())[0]
|
96
|
+
stats[name] = value
|
97
|
+
|
98
|
+
# Calculate overall progress
|
99
|
+
total_interviews = len(self.jobs_runner.total_interviews)
|
100
|
+
completed = len(self.completed_interviews)
|
101
|
+
|
102
|
+
# Get model-specific progress
|
103
|
+
model_progress = {}
|
104
|
+
for model in self.distinct_models:
|
105
|
+
completed_for_model = len(self.completed_interview_by_model[model])
|
106
|
+
target_for_model = int(
|
107
|
+
self.num_total_interviews / len(self.distinct_models)
|
108
|
+
)
|
109
|
+
model_progress[model] = {
|
110
|
+
"completed": completed_for_model,
|
111
|
+
"total": target_for_model,
|
112
|
+
"percent": (
|
113
|
+
(completed_for_model / target_for_model * 100)
|
114
|
+
if target_for_model > 0
|
115
|
+
else 0
|
116
|
+
),
|
117
|
+
}
|
118
|
+
|
119
|
+
status_dict = {
|
120
|
+
"overall_progress": {
|
121
|
+
"completed": completed,
|
122
|
+
"total": total_interviews,
|
123
|
+
"percent": (
|
124
|
+
(completed / total_interviews * 100) if total_interviews > 0 else 0
|
125
|
+
),
|
126
|
+
},
|
127
|
+
"language_model_progress": model_progress,
|
128
|
+
"statistics": stats,
|
129
|
+
"status": "completed" if completed >= total_interviews else "running",
|
130
|
+
}
|
131
|
+
|
132
|
+
model_queues = {}
|
133
|
+
for model, bucket in self.jobs_runner.bucket_collection.items():
|
134
|
+
model_name = model.model
|
135
|
+
model_queues[model_name] = {
|
136
|
+
"language_model_name": model_name,
|
137
|
+
"requests_bucket": {
|
138
|
+
"completed": bucket.requests_bucket.num_released,
|
139
|
+
"requested": bucket.requests_bucket.num_requests,
|
140
|
+
"tokens_returned": bucket.requests_bucket.tokens_returned,
|
141
|
+
"target_rate": round(bucket.requests_bucket.target_rate, 1),
|
142
|
+
"current_rate": round(bucket.requests_bucket.get_throughput(), 1),
|
143
|
+
},
|
144
|
+
"tokens_bucket": {
|
145
|
+
"completed": bucket.tokens_bucket.num_released,
|
146
|
+
"requested": bucket.tokens_bucket.num_requests,
|
147
|
+
"tokens_returned": bucket.tokens_bucket.tokens_returned,
|
148
|
+
"target_rate": round(bucket.tokens_bucket.target_rate, 1),
|
149
|
+
"current_rate": round(bucket.tokens_bucket.get_throughput(), 1),
|
150
|
+
},
|
151
|
+
}
|
152
|
+
status_dict["language_model_queues"] = model_queues
|
153
|
+
return status_dict
|
154
|
+
|
155
|
+
@abstractmethod
|
156
|
+
def setup(self):
|
157
|
+
"""
|
158
|
+
Conducts any setup that needs to happen prior to sending status updates.
|
159
|
+
|
160
|
+
Ex. For a local job, creates a job in the Coop database.
|
161
|
+
"""
|
162
|
+
pass
|
163
|
+
|
164
|
+
@abstractmethod
|
165
|
+
def send_status_update(self):
|
166
|
+
"""
|
167
|
+
Updates the current status of the job.
|
168
|
+
"""
|
169
|
+
pass
|
170
|
+
|
83
171
|
def add_completed_interview(self, result):
|
84
172
|
self.completed_interviews.append(result.interview_hash)
|
85
173
|
|
@@ -150,180 +238,90 @@ class JobsRunnerStatus:
|
|
150
238
|
}
|
151
239
|
return stat_definitions[stat_name]()
|
152
240
|
|
153
|
-
def
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
TaskProgressColumn(),
|
158
|
-
TextColumn("{task.completed}/{task.total}"),
|
159
|
-
)
|
241
|
+
def update_progress(self, stop_event):
|
242
|
+
while not stop_event.is_set():
|
243
|
+
self.send_status_update()
|
244
|
+
time.sleep(self.refresh_rate)
|
160
245
|
|
161
|
-
|
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
|
-
)
|
246
|
+
self.send_status_update()
|
232
247
|
|
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
248
|
|
255
|
-
|
249
|
+
class JobsRunnerStatus(JobsRunnerStatusBase):
|
250
|
+
@property
|
251
|
+
def create_url(self) -> str:
|
252
|
+
return f"{self.base_url}/api/v0/local-job"
|
256
253
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
table.add_column("Value", justify="right")
|
254
|
+
@property
|
255
|
+
def viewing_url(self) -> str:
|
256
|
+
return f"{self.base_url}/home/local-job-progress/{str(self.job_uuid)}"
|
261
257
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
table.add_row(pretty_name, value)
|
266
|
-
return table
|
258
|
+
@property
|
259
|
+
def update_url(self) -> str:
|
260
|
+
return f"{self.base_url}/api/v0/local-job/{str(self.job_uuid)}"
|
267
261
|
|
268
|
-
def
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
layout["metrics"].update(
|
318
|
-
Panel(
|
319
|
-
self.generate_metrics_table(),
|
320
|
-
title="Final Metrics",
|
321
|
-
border_style="magenta",
|
322
|
-
box=box.ROUNDED,
|
323
|
-
)
|
262
|
+
def setup(self) -> None:
|
263
|
+
"""
|
264
|
+
Creates a local job on Coop if one does not already exist.
|
265
|
+
"""
|
266
|
+
|
267
|
+
headers = {"Content-Type": "application/json"}
|
268
|
+
|
269
|
+
if self.api_key:
|
270
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
271
|
+
else:
|
272
|
+
headers["Authorization"] = f"Bearer None"
|
273
|
+
|
274
|
+
if self.job_uuid is None:
|
275
|
+
# Create a new local job
|
276
|
+
response = requests.post(
|
277
|
+
self.create_url,
|
278
|
+
headers=headers,
|
279
|
+
timeout=1,
|
280
|
+
)
|
281
|
+
response.raise_for_status()
|
282
|
+
data = response.json()
|
283
|
+
self.job_uuid = data.get("job_uuid")
|
284
|
+
|
285
|
+
print(f"Running with progress bar. View progress at {self.viewing_url}")
|
286
|
+
|
287
|
+
def send_status_update(self) -> None:
|
288
|
+
"""
|
289
|
+
Sends current status to the web endpoint using the instance's job_uuid.
|
290
|
+
"""
|
291
|
+
try:
|
292
|
+
# Get the status dictionary and add the job_id
|
293
|
+
status_dict = self.get_status_dict()
|
294
|
+
|
295
|
+
# Make the UUID JSON serializable
|
296
|
+
status_dict["job_id"] = str(self.job_uuid)
|
297
|
+
|
298
|
+
headers = {"Content-Type": "application/json"}
|
299
|
+
|
300
|
+
if self.api_key:
|
301
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
302
|
+
else:
|
303
|
+
headers["Authorization"] = f"Bearer None"
|
304
|
+
|
305
|
+
# Send the update
|
306
|
+
response = requests.patch(
|
307
|
+
self.update_url,
|
308
|
+
json=status_dict,
|
309
|
+
headers=headers,
|
310
|
+
timeout=1,
|
324
311
|
)
|
325
|
-
|
326
|
-
|
312
|
+
response.raise_for_status()
|
313
|
+
except requests.exceptions.RequestException as e:
|
314
|
+
print(f"Failed to send status update for job {self.job_uuid}: {e}")
|
315
|
+
|
316
|
+
def has_ep_api_key(self) -> bool:
|
317
|
+
"""
|
318
|
+
Returns True if the user has an Expected Parrot API key. Otherwise, returns False.
|
319
|
+
"""
|
320
|
+
|
321
|
+
if self.api_key is not None:
|
322
|
+
return True
|
323
|
+
else:
|
324
|
+
return False
|
327
325
|
|
328
326
|
|
329
327
|
if __name__ == "__main__":
|
edsl/jobs/tasks/TaskHistory.py
CHANGED
@@ -8,7 +8,12 @@ from edsl.jobs.tasks.task_status_enum import TaskStatus
|
|
8
8
|
|
9
9
|
|
10
10
|
class TaskHistory:
|
11
|
-
def __init__(
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
interviews: List["Interview"],
|
14
|
+
include_traceback: bool = False,
|
15
|
+
max_interviews: int = 10,
|
16
|
+
):
|
12
17
|
"""
|
13
18
|
The structure of a TaskHistory exception
|
14
19
|
|
@@ -22,6 +27,7 @@ class TaskHistory:
|
|
22
27
|
self.include_traceback = include_traceback
|
23
28
|
|
24
29
|
self._interviews = {index: i for index, i in enumerate(self.total_interviews)}
|
30
|
+
self.max_interviews = max_interviews
|
25
31
|
|
26
32
|
@classmethod
|
27
33
|
def example(cls):
|
@@ -73,19 +79,21 @@ class TaskHistory:
|
|
73
79
|
"""Return a string representation of the TaskHistory."""
|
74
80
|
return f"TaskHistory(interviews={self.total_interviews})."
|
75
81
|
|
76
|
-
def to_dict(self):
|
82
|
+
def to_dict(self, add_edsl_version=True):
|
77
83
|
"""Return the TaskHistory as a dictionary."""
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
# "indices": self.indices,
|
84
|
-
# }
|
85
|
-
return {
|
86
|
-
"interviews": [i._to_dict() for i in self.total_interviews],
|
84
|
+
d = {
|
85
|
+
"interviews": [
|
86
|
+
i.to_dict(add_edsl_version=add_edsl_version)
|
87
|
+
for i in self.total_interviews
|
88
|
+
],
|
87
89
|
"include_traceback": self.include_traceback,
|
88
90
|
}
|
91
|
+
if add_edsl_version:
|
92
|
+
from edsl import __version__
|
93
|
+
|
94
|
+
d["edsl_version"] = __version__
|
95
|
+
d["edsl_class_name"] = "TaskHistory"
|
96
|
+
return d
|
89
97
|
|
90
98
|
@classmethod
|
91
99
|
def from_dict(cls, data: dict):
|
@@ -115,10 +123,11 @@ class TaskHistory:
|
|
115
123
|
|
116
124
|
def _repr_html_(self):
|
117
125
|
"""Return an HTML representation of the TaskHistory."""
|
118
|
-
|
126
|
+
d = self.to_dict(add_edsl_version=False)
|
127
|
+
data = [[k, v] for k, v in d.items()]
|
128
|
+
from tabulate import tabulate
|
119
129
|
|
120
|
-
|
121
|
-
return data_to_html(newdata, replace_new_lines=True)
|
130
|
+
return tabulate(data, headers=["keys", "values"], tablefmt="html")
|
122
131
|
|
123
132
|
def show_exceptions(self, tracebacks=False):
|
124
133
|
"""Print the exceptions."""
|
@@ -248,8 +257,6 @@ class TaskHistory:
|
|
248
257
|
for question_name, exceptions in interview.exceptions.items():
|
249
258
|
for exception in exceptions:
|
250
259
|
exception_type = exception.exception.__class__.__name__
|
251
|
-
# exception_type = exception["exception"]
|
252
|
-
# breakpoint()
|
253
260
|
if exception_type in exceptions_by_type:
|
254
261
|
exceptions_by_type[exception_type] += 1
|
255
262
|
else:
|
@@ -336,9 +343,9 @@ class TaskHistory:
|
|
336
343
|
|
337
344
|
env = Environment(loader=TemplateLoader("edsl", "templates/error_reporting"))
|
338
345
|
|
339
|
-
#
|
346
|
+
# Get current memory usage at this point
|
347
|
+
|
340
348
|
template = env.get_template("base.html")
|
341
|
-
# rendered_template = template.render(your_data=your_data)
|
342
349
|
|
343
350
|
# Render the template with data
|
344
351
|
output = template.render(
|
@@ -352,6 +359,7 @@ class TaskHistory:
|
|
352
359
|
exceptions_by_model=self.exceptions_by_model,
|
353
360
|
exceptions_by_service=self.exceptions_by_service,
|
354
361
|
models_used=models_used,
|
362
|
+
max_interviews=self.max_interviews,
|
355
363
|
)
|
356
364
|
return output
|
357
365
|
|
@@ -361,7 +369,7 @@ class TaskHistory:
|
|
361
369
|
return_link=False,
|
362
370
|
css=None,
|
363
371
|
cta="Open Report in New Tab",
|
364
|
-
open_in_browser=
|
372
|
+
open_in_browser=False,
|
365
373
|
):
|
366
374
|
"""Return an HTML report."""
|
367
375
|
|