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.
Files changed (86) hide show
  1. edsl/Base.py +63 -34
  2. edsl/BaseDiff.py +7 -7
  3. edsl/__init__.py +2 -1
  4. edsl/__version__.py +1 -1
  5. edsl/agents/Agent.py +23 -11
  6. edsl/agents/AgentList.py +86 -23
  7. edsl/agents/Invigilator.py +18 -7
  8. edsl/agents/InvigilatorBase.py +0 -19
  9. edsl/agents/PromptConstructor.py +5 -4
  10. edsl/auto/SurveyCreatorPipeline.py +1 -1
  11. edsl/auto/utilities.py +1 -1
  12. edsl/base/Base.py +3 -13
  13. edsl/config.py +8 -0
  14. edsl/coop/coop.py +89 -19
  15. edsl/data/Cache.py +45 -17
  16. edsl/data/CacheEntry.py +8 -3
  17. edsl/data/RemoteCacheSync.py +0 -19
  18. edsl/enums.py +2 -0
  19. edsl/exceptions/agents.py +4 -0
  20. edsl/exceptions/cache.py +5 -0
  21. edsl/inference_services/GoogleService.py +7 -15
  22. edsl/inference_services/PerplexityService.py +163 -0
  23. edsl/inference_services/registry.py +2 -0
  24. edsl/jobs/Jobs.py +110 -559
  25. edsl/jobs/JobsChecks.py +147 -0
  26. edsl/jobs/JobsPrompts.py +268 -0
  27. edsl/jobs/JobsRemoteInferenceHandler.py +239 -0
  28. edsl/jobs/buckets/TokenBucket.py +3 -0
  29. edsl/jobs/interviews/Interview.py +7 -7
  30. edsl/jobs/runners/JobsRunnerAsyncio.py +156 -28
  31. edsl/jobs/runners/JobsRunnerStatus.py +194 -196
  32. edsl/jobs/tasks/TaskHistory.py +27 -19
  33. edsl/language_models/LanguageModel.py +52 -90
  34. edsl/language_models/ModelList.py +67 -14
  35. edsl/language_models/registry.py +57 -4
  36. edsl/notebooks/Notebook.py +7 -8
  37. edsl/prompts/Prompt.py +8 -3
  38. edsl/questions/QuestionBase.py +38 -30
  39. edsl/questions/QuestionBaseGenMixin.py +1 -1
  40. edsl/questions/QuestionBasePromptsMixin.py +0 -17
  41. edsl/questions/QuestionExtract.py +3 -4
  42. edsl/questions/QuestionFunctional.py +10 -3
  43. edsl/questions/derived/QuestionTopK.py +2 -0
  44. edsl/questions/question_registry.py +36 -6
  45. edsl/results/CSSParameterizer.py +108 -0
  46. edsl/results/Dataset.py +146 -15
  47. edsl/results/DatasetExportMixin.py +231 -217
  48. edsl/results/DatasetTree.py +134 -4
  49. edsl/results/Result.py +31 -16
  50. edsl/results/Results.py +159 -65
  51. edsl/results/TableDisplay.py +198 -0
  52. edsl/results/table_display.css +78 -0
  53. edsl/scenarios/FileStore.py +187 -13
  54. edsl/scenarios/Scenario.py +73 -18
  55. edsl/scenarios/ScenarioJoin.py +127 -0
  56. edsl/scenarios/ScenarioList.py +251 -76
  57. edsl/surveys/MemoryPlan.py +1 -1
  58. edsl/surveys/Rule.py +1 -5
  59. edsl/surveys/RuleCollection.py +1 -1
  60. edsl/surveys/Survey.py +25 -19
  61. edsl/surveys/SurveyFlowVisualizationMixin.py +67 -9
  62. edsl/surveys/instructions/ChangeInstruction.py +9 -7
  63. edsl/surveys/instructions/Instruction.py +21 -7
  64. edsl/templates/error_reporting/interview_details.html +3 -3
  65. edsl/templates/error_reporting/interviews.html +18 -9
  66. edsl/{conjure → utilities}/naming_utilities.py +1 -1
  67. edsl/utilities/utilities.py +15 -0
  68. {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/METADATA +2 -1
  69. {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/RECORD +71 -77
  70. edsl/conjure/AgentConstructionMixin.py +0 -160
  71. edsl/conjure/Conjure.py +0 -62
  72. edsl/conjure/InputData.py +0 -659
  73. edsl/conjure/InputDataCSV.py +0 -48
  74. edsl/conjure/InputDataMixinQuestionStats.py +0 -182
  75. edsl/conjure/InputDataPyRead.py +0 -91
  76. edsl/conjure/InputDataSPSS.py +0 -8
  77. edsl/conjure/InputDataStata.py +0 -8
  78. edsl/conjure/QuestionOptionMixin.py +0 -76
  79. edsl/conjure/QuestionTypeMixin.py +0 -23
  80. edsl/conjure/RawQuestion.py +0 -65
  81. edsl/conjure/SurveyResponses.py +0 -7
  82. edsl/conjure/__init__.py +0 -9
  83. edsl/conjure/examples/placeholder.txt +0 -0
  84. edsl/conjure/utilities.py +0 -201
  85. {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/LICENSE +0 -0
  86. {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/WHEEL +0 -0
@@ -144,216 +144,216 @@ class DatasetExportMixin:
144
144
  for value in list_of_values:
145
145
  print(f"{key}: {value}")
146
146
 
147
- def print(
148
- self,
149
- pretty_labels: Optional[dict] = None,
150
- filename: Optional[str] = None,
151
- format: Optional[Literal["rich", "html", "markdown", "latex"]] = None,
152
- interactive: bool = False,
153
- split_at_dot: bool = True,
154
- max_rows=None,
155
- tee=False,
156
- iframe=False,
157
- iframe_height: int = 200,
158
- iframe_width: int = 600,
159
- web=False,
160
- return_string: bool = False,
161
- ) -> Union[None, str, "Results"]:
162
- """Print the results in a pretty format.
163
-
164
- :param pretty_labels: A dictionary of pretty labels for the columns.
165
- :param filename: The filename to save the results to.
166
- :param format: The format to print the results in. Options are 'rich', 'html', 'markdown', or 'latex'.
167
- :param interactive: Whether to print the results interactively in a Jupyter notebook.
168
- :param split_at_dot: Whether to split the column names at the last dot w/ a newline.
169
- :param max_rows: The maximum number of rows to print.
170
- :param tee: Whether to return the dataset.
171
- :param iframe: Whether to display the table in an iframe.
172
- :param iframe_height: The height of the iframe.
173
- :param iframe_width: The width of the iframe.
174
- :param web: Whether to display the table in a web browser.
175
- :param return_string: Whether to return the output as a string instead of printing.
176
-
177
- :return: None if tee is False and return_string is False, the dataset if tee is True, or a string if return_string is True.
178
-
179
- Example: Print in rich format at the terminal
180
-
181
- >>> from edsl.results import Results
182
- >>> r = Results.example()
183
- >>> r.select('how_feeling').print(format = "rich")
184
- ┏━━━━━━━━━━━━━━┓
185
- ┃ answer ┃
186
- ┃ .how_feeling ┃
187
- ┡━━━━━━━━━━━━━━┩
188
- │ OK │
189
- ├──────────────┤
190
- │ Great │
191
- ├──────────────┤
192
- │ Terrible │
193
- ├──────────────┤
194
- │ OK │
195
- └──────────────┘
196
-
197
- >>> r = Results.example()
198
- >>> r2 = r.select("how_feeling").print(format = "rich", tee = True, max_rows = 2)
199
- ┏━━━━━━━━━━━━━━┓
200
- ┃ answer ┃
201
- ┃ .how_feeling ┃
202
- ┡━━━━━━━━━━━━━━┩
203
- │ OK │
204
- ├──────────────┤
205
- │ Great │
206
- └──────────────┘
207
- >>> r2
208
- Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
209
-
210
- >>> r.select('how_feeling').print(format = "rich", max_rows = 2)
211
- ┏━━━━━━━━━━━━━━┓
212
- ┃ answer ┃
213
- ┃ .how_feeling ┃
214
- ┡━━━━━━━━━━━━━━┩
215
- │ OK │
216
- ├──────────────┤
217
- │ Great │
218
- └──────────────┘
219
-
220
- >>> r.select('how_feeling').print(format = "rich", split_at_dot = False)
221
- ┏━━━━━━━━━━━━━━━━━━━━┓
222
- ┃ answer.how_feeling ┃
223
- ┡━━━━━━━━━━━━━━━━━━━━┩
224
- │ OK │
225
- ├────────────────────┤
226
- │ Great │
227
- ├────────────────────┤
228
- │ Terrible │
229
- ├────────────────────┤
230
- │ OK │
231
- └────────────────────┘
232
-
233
- Example: using the pretty_labels parameter
234
-
235
- >>> r.select('how_feeling').print(format="rich", pretty_labels = {'answer.how_feeling': "How are you feeling"})
236
- ┏━━━━━━━━━━━━━━━━━━━━━┓
237
- ┃ How are you feeling ┃
238
- ┡━━━━━━━━━━━━━━━━━━━━━┩
239
- │ OK │
240
- ├─────────────────────┤
241
- │ Great │
242
- ├─────────────────────┤
243
- │ Terrible │
244
- ├─────────────────────┤
245
- │ OK │
246
- └─────────────────────┘
247
-
248
- Example: printing in markdown format
249
-
250
- >>> r.select('how_feeling').print(format='markdown')
251
- | answer.how_feeling |
252
- |--|
253
- | OK |
254
- | Great |
255
- | Terrible |
256
- | OK |
257
- ...
258
-
259
- >>> r.select('how_feeling').print(format='latex')
260
- \\begin{tabular}{l}
261
- ...
262
- \\end{tabular}
263
- <BLANKLINE>
264
- """
265
- from IPython.display import HTML, display
266
- from edsl.utilities.utilities import is_notebook
267
- import io
268
- import sys
269
-
270
- def _determine_format(format):
271
- if format is None:
272
- if is_notebook():
273
- format = "html"
274
- else:
275
- format = "rich"
276
- if format not in ["rich", "html", "markdown", "latex"]:
277
- raise ValueError(
278
- "format must be one of 'rich', 'html', 'markdown', or 'latex'."
279
- )
280
-
281
- return format
282
-
283
- format = _determine_format(format)
284
-
285
- if pretty_labels is None:
286
- pretty_labels = {}
287
-
288
- if pretty_labels != {}: # only split at dot if there are no pretty labels
289
- split_at_dot = False
290
-
291
- def _create_data():
292
- for index, entry in enumerate(self):
293
- key, list_of_values = list(entry.items())[0]
294
- yield {pretty_labels.get(key, key): list_of_values[:max_rows]}
295
-
296
- new_data = list(_create_data())
297
-
298
- # Capture output if return_string is True
299
- if return_string:
300
- old_stdout = sys.stdout
301
- sys.stdout = io.StringIO()
302
-
303
- output = None
304
-
305
- if format == "rich":
306
- from edsl.utilities.interface import print_dataset_with_rich
307
-
308
- output = print_dataset_with_rich(
309
- new_data, filename=filename, split_at_dot=split_at_dot
310
- )
311
- elif format == "markdown":
312
- from edsl.utilities.interface import print_list_of_dicts_as_markdown_table
313
-
314
- output = print_list_of_dicts_as_markdown_table(new_data, filename=filename)
315
- elif format == "latex":
316
- df = self.to_pandas()
317
- df.columns = [col.replace("_", " ") for col in df.columns]
318
- latex_string = df.to_latex(index=False)
319
-
320
- if filename is not None:
321
- with open(filename, "w") as f:
322
- f.write(latex_string)
323
- else:
324
- print(latex_string)
325
- output = latex_string
326
- elif format == "html":
327
- from edsl.utilities.interface import print_list_of_dicts_as_html_table
328
-
329
- html_source = print_list_of_dicts_as_html_table(
330
- new_data, interactive=interactive
331
- )
332
-
333
- if iframe:
334
- iframe = f""""
335
- <iframe srcdoc="{ html.escape(html_source) }" style="width: {iframe_width}px; height: {iframe_height}px;"></iframe>
336
- """
337
- display(HTML(iframe))
338
- elif is_notebook():
339
- display(HTML(html_source))
340
- else:
341
- from edsl.utilities.interface import view_html
342
-
343
- view_html(html_source)
344
-
345
- output = html_source
346
-
347
- # Restore stdout and get captured output if return_string is True
348
- if return_string:
349
- captured_output = sys.stdout.getvalue()
350
- sys.stdout = old_stdout
351
- return captured_output or output
352
-
353
- if tee:
354
- return self
355
-
356
- return None
147
+ # def print(
148
+ # self,
149
+ # pretty_labels: Optional[dict] = None,
150
+ # filename: Optional[str] = None,
151
+ # format: Optional[Literal["rich", "html", "markdown", "latex"]] = None,
152
+ # interactive: bool = False,
153
+ # split_at_dot: bool = True,
154
+ # max_rows=None,
155
+ # tee=False,
156
+ # iframe=False,
157
+ # iframe_height: int = 200,
158
+ # iframe_width: int = 600,
159
+ # web=False,
160
+ # return_string: bool = False,
161
+ # ) -> Union[None, str, "Results"]:
162
+ # """Print the results in a pretty format.
163
+
164
+ # :param pretty_labels: A dictionary of pretty labels for the columns.
165
+ # :param filename: The filename to save the results to.
166
+ # :param format: The format to print the results in. Options are 'rich', 'html', 'markdown', or 'latex'.
167
+ # :param interactive: Whether to print the results interactively in a Jupyter notebook.
168
+ # :param split_at_dot: Whether to split the column names at the last dot w/ a newline.
169
+ # :param max_rows: The maximum number of rows to print.
170
+ # :param tee: Whether to return the dataset.
171
+ # :param iframe: Whether to display the table in an iframe.
172
+ # :param iframe_height: The height of the iframe.
173
+ # :param iframe_width: The width of the iframe.
174
+ # :param web: Whether to display the table in a web browser.
175
+ # :param return_string: Whether to return the output as a string instead of printing.
176
+
177
+ # :return: None if tee is False and return_string is False, the dataset if tee is True, or a string if return_string is True.
178
+
179
+ # Example: Print in rich format at the terminal
180
+
181
+ # >>> from edsl.results import Results
182
+ # >>> r = Results.example()
183
+ # >>> r.select('how_feeling').print(format = "rich")
184
+ # ┏━━━━━━━━━━━━━━┓
185
+ # ┃ answer ┃
186
+ # ┃ .how_feeling ┃
187
+ # ┡━━━━━━━━━━━━━━┩
188
+ # │ OK │
189
+ # ├──────────────┤
190
+ # │ Great │
191
+ # ├──────────────┤
192
+ # │ Terrible │
193
+ # ├──────────────┤
194
+ # │ OK │
195
+ # └──────────────┘
196
+
197
+ # >>> r = Results.example()
198
+ # >>> r2 = r.select("how_feeling").print(format = "rich", tee = True, max_rows = 2)
199
+ # ┏━━━━━━━━━━━━━━┓
200
+ # ┃ answer ┃
201
+ # ┃ .how_feeling ┃
202
+ # ┡━━━━━━━━━━━━━━┩
203
+ # │ OK │
204
+ # ├──────────────┤
205
+ # │ Great │
206
+ # └──────────────┘
207
+ # >>> r2
208
+ # Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
209
+
210
+ # >>> r.select('how_feeling').print(format = "rich", max_rows = 2)
211
+ # ┏━━━━━━━━━━━━━━┓
212
+ # ┃ answer ┃
213
+ # ┃ .how_feeling ┃
214
+ # ┡━━━━━━━━━━━━━━┩
215
+ # │ OK │
216
+ # ├──────────────┤
217
+ # │ Great │
218
+ # └──────────────┘
219
+
220
+ # >>> r.select('how_feeling').print(format = "rich", split_at_dot = False)
221
+ # ┏━━━━━━━━━━━━━━━━━━━━┓
222
+ # ┃ answer.how_feeling ┃
223
+ # ┡━━━━━━━━━━━━━━━━━━━━┩
224
+ # │ OK │
225
+ # ├────────────────────┤
226
+ # │ Great │
227
+ # ├────────────────────┤
228
+ # │ Terrible │
229
+ # ├────────────────────┤
230
+ # │ OK │
231
+ # └────────────────────┘
232
+
233
+ # Example: using the pretty_labels parameter
234
+
235
+ # >>> r.select('how_feeling').print(format="rich", pretty_labels = {'answer.how_feeling': "How are you feeling"})
236
+ # ┏━━━━━━━━━━━━━━━━━━━━━┓
237
+ # ┃ How are you feeling ┃
238
+ # ┡━━━━━━━━━━━━━━━━━━━━━┩
239
+ # │ OK │
240
+ # ├─────────────────────┤
241
+ # │ Great │
242
+ # ├─────────────────────┤
243
+ # │ Terrible │
244
+ # ├─────────────────────┤
245
+ # │ OK │
246
+ # └─────────────────────┘
247
+
248
+ # Example: printing in markdown format
249
+
250
+ # >>> r.select('how_feeling').print(format='markdown')
251
+ # | answer.how_feeling |
252
+ # |--|
253
+ # | OK |
254
+ # | Great |
255
+ # | Terrible |
256
+ # | OK |
257
+ # ...
258
+
259
+ # >>> r.select('how_feeling').print(format='latex')
260
+ # \\begin{tabular}{l}
261
+ # ...
262
+ # \\end{tabular}
263
+ # <BLANKLINE>
264
+ # """
265
+ # from IPython.display import HTML, display
266
+ # from edsl.utilities.utilities import is_notebook
267
+ # import io
268
+ # import sys
269
+
270
+ # def _determine_format(format):
271
+ # if format is None:
272
+ # if is_notebook():
273
+ # format = "html"
274
+ # else:
275
+ # format = "rich"
276
+ # if format not in ["rich", "html", "markdown", "latex"]:
277
+ # raise ValueError(
278
+ # "format must be one of 'rich', 'html', 'markdown', or 'latex'."
279
+ # )
280
+
281
+ # return format
282
+
283
+ # format = _determine_format(format)
284
+
285
+ # if pretty_labels is None:
286
+ # pretty_labels = {}
287
+
288
+ # if pretty_labels != {}: # only split at dot if there are no pretty labels
289
+ # split_at_dot = False
290
+
291
+ # def _create_data():
292
+ # for index, entry in enumerate(self):
293
+ # key, list_of_values = list(entry.items())[0]
294
+ # yield {pretty_labels.get(key, key): list_of_values[:max_rows]}
295
+
296
+ # new_data = list(_create_data())
297
+
298
+ # # Capture output if return_string is True
299
+ # if return_string:
300
+ # old_stdout = sys.stdout
301
+ # sys.stdout = io.StringIO()
302
+
303
+ # output = None
304
+
305
+ # if format == "rich":
306
+ # from edsl.utilities.interface import print_dataset_with_rich
307
+
308
+ # output = print_dataset_with_rich(
309
+ # new_data, filename=filename, split_at_dot=split_at_dot
310
+ # )
311
+ # elif format == "markdown":
312
+ # from edsl.utilities.interface import print_list_of_dicts_as_markdown_table
313
+
314
+ # output = print_list_of_dicts_as_markdown_table(new_data, filename=filename)
315
+ # elif format == "latex":
316
+ # df = self.to_pandas()
317
+ # df.columns = [col.replace("_", " ") for col in df.columns]
318
+ # latex_string = df.to_latex(index=False)
319
+
320
+ # if filename is not None:
321
+ # with open(filename, "w") as f:
322
+ # f.write(latex_string)
323
+ # else:
324
+ # print(latex_string)
325
+ # output = latex_string
326
+ # elif format == "html":
327
+ # from edsl.utilities.interface import print_list_of_dicts_as_html_table
328
+
329
+ # html_source = print_list_of_dicts_as_html_table(
330
+ # new_data, interactive=interactive
331
+ # )
332
+
333
+ # if iframe:
334
+ # iframe = f""""
335
+ # <iframe srcdoc="{ html.escape(html_source) }" style="width: {iframe_width}px; height: {iframe_height}px;"></iframe>
336
+ # """
337
+ # display(HTML(iframe))
338
+ # elif is_notebook():
339
+ # display(HTML(html_source))
340
+ # else:
341
+ # from edsl.utilities.interface import view_html
342
+
343
+ # view_html(html_source)
344
+
345
+ # output = html_source
346
+
347
+ # # Restore stdout and get captured output if return_string is True
348
+ # if return_string:
349
+ # captured_output = sys.stdout.getvalue()
350
+ # sys.stdout = old_stdout
351
+ # return captured_output or output
352
+
353
+ # if tee:
354
+ # return self
355
+
356
+ # return None
357
357
 
358
358
  def to_csv(
359
359
  self,
@@ -405,6 +405,7 @@ class DatasetExportMixin:
405
405
  writer = csv.writer(f)
406
406
  writer.writerow(header)
407
407
  writer.writerows(rows)
408
+ # print(f"Saved to {filename}")
408
409
  else:
409
410
  output = io.StringIO()
410
411
  writer = csv.writer(output)
@@ -515,7 +516,14 @@ class DatasetExportMixin:
515
516
  from edsl import AgentList, Agent
516
517
 
517
518
  list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
518
- return AgentList([Agent(d) for d in list_of_dicts])
519
+ agents = []
520
+ for d in list_of_dicts:
521
+ if "name" in d:
522
+ d["agent_name"] = d.pop("name")
523
+ agents.append(Agent(d, name=d["agent_name"]))
524
+ else:
525
+ agents.append(Agent(d))
526
+ return AgentList(agents)
519
527
 
520
528
  def to_dicts(self, remove_prefix: bool = True) -> list[dict]:
521
529
  """Convert the results to a list of dictionaries.
@@ -651,9 +659,7 @@ class DatasetExportMixin:
651
659
  >>> r.select('how_feeling').tally('answer.how_feeling', output = "dict")
652
660
  {'OK': 2, 'Great': 1, 'Terrible': 1}
653
661
  >>> r.select('how_feeling').tally('answer.how_feeling', output = "Dataset")
654
- Dataset([{'value': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
655
- >>> r.select('how_feeling', 'period').tally('how_feeling', 'period', output = "dict")
656
- {('OK', 'morning'): 1, ('Great', 'afternoon'): 1, ('Terrible', 'morning'): 1, ('OK', 'afternoon'): 1}
662
+ Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible']}, {'count': [2, 1, 1]}])
657
663
  """
658
664
  from collections import Counter
659
665
 
@@ -664,8 +670,6 @@ class DatasetExportMixin:
664
670
  column.split(".")[-1] for column in self.relevant_columns()
665
671
  ]
666
672
 
667
- # breakpoint()
668
-
669
673
  if not all(
670
674
  f in self.relevant_columns() or f in relevant_columns_without_prefix
671
675
  for f in fields
@@ -703,12 +707,22 @@ class DatasetExportMixin:
703
707
  )
704
708
  return sorted_tally
705
709
  elif output == "Dataset":
706
- return Dataset(
710
+ dataset = Dataset(
707
711
  [
708
712
  {"value": list(sorted_tally.keys())},
709
713
  {"count": list(sorted_tally.values())},
710
714
  ]
711
715
  )
716
+ # return dataset
717
+ sl = dataset.to_scenario_list().unpack(
718
+ "value",
719
+ new_names=[fields] if isinstance(fields, str) else fields,
720
+ keep_original=False,
721
+ )
722
+ keys = list(sl[0].keys())
723
+ keys.remove("count")
724
+ keys.append("count")
725
+ return sl.reorder_keys(keys).to_dataset()
712
726
 
713
727
 
714
728
  if __name__ == "__main__":