edsl 0.1.38__py3-none-any.whl → 0.1.38.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.
Files changed (86) hide show
  1. edsl/Base.py +34 -63
  2. edsl/BaseDiff.py +7 -7
  3. edsl/__init__.py +1 -2
  4. edsl/__version__.py +1 -1
  5. edsl/agents/Agent.py +11 -23
  6. edsl/agents/AgentList.py +23 -86
  7. edsl/agents/Invigilator.py +7 -18
  8. edsl/agents/InvigilatorBase.py +19 -0
  9. edsl/agents/PromptConstructor.py +4 -5
  10. edsl/auto/SurveyCreatorPipeline.py +1 -1
  11. edsl/auto/utilities.py +1 -1
  12. edsl/base/Base.py +13 -3
  13. edsl/config.py +0 -8
  14. edsl/conjure/AgentConstructionMixin.py +160 -0
  15. edsl/conjure/Conjure.py +62 -0
  16. edsl/conjure/InputData.py +659 -0
  17. edsl/conjure/InputDataCSV.py +48 -0
  18. edsl/conjure/InputDataMixinQuestionStats.py +182 -0
  19. edsl/conjure/InputDataPyRead.py +91 -0
  20. edsl/conjure/InputDataSPSS.py +8 -0
  21. edsl/conjure/InputDataStata.py +8 -0
  22. edsl/conjure/QuestionOptionMixin.py +76 -0
  23. edsl/conjure/QuestionTypeMixin.py +23 -0
  24. edsl/conjure/RawQuestion.py +65 -0
  25. edsl/conjure/SurveyResponses.py +7 -0
  26. edsl/conjure/__init__.py +9 -0
  27. edsl/conjure/examples/placeholder.txt +0 -0
  28. edsl/{utilities → conjure}/naming_utilities.py +1 -1
  29. edsl/conjure/utilities.py +201 -0
  30. edsl/coop/coop.py +7 -77
  31. edsl/data/Cache.py +17 -45
  32. edsl/data/CacheEntry.py +3 -8
  33. edsl/data/RemoteCacheSync.py +19 -0
  34. edsl/enums.py +0 -2
  35. edsl/exceptions/agents.py +0 -4
  36. edsl/inference_services/GoogleService.py +15 -7
  37. edsl/inference_services/registry.py +0 -2
  38. edsl/jobs/Jobs.py +559 -110
  39. edsl/jobs/buckets/TokenBucket.py +0 -3
  40. edsl/jobs/interviews/Interview.py +7 -7
  41. edsl/jobs/runners/JobsRunnerAsyncio.py +28 -156
  42. edsl/jobs/runners/JobsRunnerStatus.py +196 -194
  43. edsl/jobs/tasks/TaskHistory.py +19 -27
  44. edsl/language_models/LanguageModel.py +90 -52
  45. edsl/language_models/ModelList.py +14 -67
  46. edsl/language_models/registry.py +4 -57
  47. edsl/notebooks/Notebook.py +8 -7
  48. edsl/prompts/Prompt.py +3 -8
  49. edsl/questions/QuestionBase.py +30 -38
  50. edsl/questions/QuestionBaseGenMixin.py +1 -1
  51. edsl/questions/QuestionBasePromptsMixin.py +17 -0
  52. edsl/questions/QuestionExtract.py +4 -3
  53. edsl/questions/QuestionFunctional.py +3 -10
  54. edsl/questions/derived/QuestionTopK.py +0 -2
  55. edsl/questions/question_registry.py +6 -36
  56. edsl/results/Dataset.py +15 -146
  57. edsl/results/DatasetExportMixin.py +217 -231
  58. edsl/results/DatasetTree.py +4 -134
  59. edsl/results/Result.py +16 -31
  60. edsl/results/Results.py +65 -159
  61. edsl/scenarios/FileStore.py +13 -187
  62. edsl/scenarios/Scenario.py +18 -73
  63. edsl/scenarios/ScenarioList.py +76 -251
  64. edsl/surveys/MemoryPlan.py +1 -1
  65. edsl/surveys/Rule.py +5 -1
  66. edsl/surveys/RuleCollection.py +1 -1
  67. edsl/surveys/Survey.py +19 -25
  68. edsl/surveys/SurveyFlowVisualizationMixin.py +9 -67
  69. edsl/surveys/instructions/ChangeInstruction.py +7 -9
  70. edsl/surveys/instructions/Instruction.py +7 -21
  71. edsl/templates/error_reporting/interview_details.html +3 -3
  72. edsl/templates/error_reporting/interviews.html +9 -18
  73. edsl/utilities/utilities.py +0 -15
  74. {edsl-0.1.38.dist-info → edsl-0.1.38.dev1.dist-info}/METADATA +1 -2
  75. {edsl-0.1.38.dist-info → edsl-0.1.38.dev1.dist-info}/RECORD +77 -71
  76. edsl/exceptions/cache.py +0 -5
  77. edsl/inference_services/PerplexityService.py +0 -163
  78. edsl/jobs/JobsChecks.py +0 -147
  79. edsl/jobs/JobsPrompts.py +0 -268
  80. edsl/jobs/JobsRemoteInferenceHandler.py +0 -239
  81. edsl/results/CSSParameterizer.py +0 -108
  82. edsl/results/TableDisplay.py +0 -198
  83. edsl/results/table_display.css +0 -78
  84. edsl/scenarios/ScenarioJoin.py +0 -127
  85. {edsl-0.1.38.dist-info → edsl-0.1.38.dev1.dist-info}/LICENSE +0 -0
  86. {edsl-0.1.38.dist-info → edsl-0.1.38.dev1.dist-info}/WHEEL +0 -0
@@ -41,20 +41,18 @@ from edsl.data_transfer_models import (
41
41
  AgentResponseDict,
42
42
  )
43
43
 
44
- if TYPE_CHECKING:
45
- from edsl.data.Cache import Cache
46
- from edsl.scenarios.FileStore import FileStore
47
- from edsl.questions.QuestionBase import QuestionBase
48
44
 
49
45
  from edsl.config import CONFIG
50
46
  from edsl.utilities.decorators import sync_wrapper, jupyter_nb_handler
51
- from edsl.utilities.decorators import remove_edsl_version
52
-
53
- from edsl.Base import PersistenceMixin
47
+ from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
48
+ from edsl.language_models.repair import repair
49
+ from edsl.enums import InferenceServiceType
50
+ from edsl.Base import RichPrintingMixin, PersistenceMixin
54
51
  from edsl.language_models.RegisterLanguageModelsMeta import RegisterLanguageModelsMeta
55
- from edsl.language_models.KeyLookup import KeyLookup
56
52
  from edsl.exceptions.language_models import LanguageModelBadResponseError
57
53
 
54
+ from edsl.language_models.KeyLookup import KeyLookup
55
+
58
56
  TIMEOUT = float(CONFIG.get("EDSL_API_TIMEOUT"))
59
57
 
60
58
 
@@ -118,11 +116,29 @@ def handle_key_error(func):
118
116
 
119
117
 
120
118
  class LanguageModel(
121
- PersistenceMixin,
122
- ABC,
123
- metaclass=RegisterLanguageModelsMeta,
119
+ RichPrintingMixin, PersistenceMixin, ABC, metaclass=RegisterLanguageModelsMeta
124
120
  ):
125
- """ABC for Language Models."""
121
+ """ABC for LLM subclasses.
122
+
123
+ TODO:
124
+
125
+ 1) Need better, more descriptive names for functions
126
+
127
+ get_model_response_no_cache (currently called async_execute_model_call)
128
+
129
+ get_model_response (currently called async_get_raw_response; uses cache & adds tracking info)
130
+ Calls:
131
+ - async_execute_model_call
132
+ - _updated_model_response_with_tracking
133
+
134
+ get_answer (currently called async_get_response)
135
+ This parses out the answer block and does some error-handling.
136
+ Calls:
137
+ - async_get_raw_response
138
+ - parse_response
139
+
140
+
141
+ """
126
142
 
127
143
  _model_ = None
128
144
  key_sequence = (
@@ -180,7 +196,7 @@ class LanguageModel(
180
196
  system_prompt = "You are a helpful agent pretending to be a human."
181
197
  return self.execute_model_call(user_prompt, system_prompt)
182
198
 
183
- def set_key_lookup(self, key_lookup: KeyLookup) -> None:
199
+ def set_key_lookup(self, key_lookup: KeyLookup):
184
200
  del self._api_token
185
201
  self.key_lookup = key_lookup
186
202
 
@@ -195,14 +211,10 @@ class LanguageModel(
195
211
  def __getitem__(self, key):
196
212
  return getattr(self, key)
197
213
 
198
- def _repr_html_(self) -> str:
199
- d = {"model": self.model}
200
- d.update(self.parameters)
201
- data = [[k, v] for k, v in d.items()]
202
- from tabulate import tabulate
214
+ def _repr_html_(self):
215
+ from edsl.utilities.utilities import data_to_html
203
216
 
204
- table = str(tabulate(data, headers=["keys", "values"], tablefmt="html"))
205
- return f"<pre>{table}</pre>"
217
+ return data_to_html(self.to_dict())
206
218
 
207
219
  def hello(self, verbose=False):
208
220
  """Runs a simple test to check if the model is working."""
@@ -223,6 +235,7 @@ class LanguageModel(
223
235
  This method is used to check if the model has a valid API key.
224
236
  """
225
237
  from edsl.enums import service_to_api_keyname
238
+ import os
226
239
 
227
240
  if self._model_ == "test":
228
241
  return True
@@ -235,9 +248,9 @@ class LanguageModel(
235
248
  """Allow the model to be used as a key in a dictionary."""
236
249
  from edsl.utilities.utilities import dict_hash
237
250
 
238
- return dict_hash(self.to_dict(add_edsl_version=False))
251
+ return dict_hash(self.to_dict())
239
252
 
240
- def __eq__(self, other) -> bool:
253
+ def __eq__(self, other):
241
254
  """Check is two models are the same.
242
255
 
243
256
  >>> m1 = LanguageModel.example()
@@ -265,11 +278,15 @@ class LanguageModel(
265
278
  @property
266
279
  def RPM(self):
267
280
  """Model's requests-per-minute limit."""
281
+ # self._set_rate_limits()
282
+ # return self._safety_factor * self.__rate_limits["rpm"]
268
283
  return self._rpm
269
284
 
270
285
  @property
271
286
  def TPM(self):
272
287
  """Model's tokens-per-minute limit."""
288
+ # self._set_rate_limits()
289
+ # return self._safety_factor * self.__rate_limits["tpm"]
273
290
  return self._tpm
274
291
 
275
292
  @property
@@ -297,6 +314,8 @@ class LanguageModel(
297
314
  >>> LanguageModel._overide_default_parameters(passed_parameter_dict={"temperature": 0.5}, default_parameter_dict={"temperature":0.9, "max_tokens": 1000})
298
315
  {'temperature': 0.5, 'max_tokens': 1000}
299
316
  """
317
+ # parameters = dict({})
318
+
300
319
  # this is the case when data is loaded from a dict after serialization
301
320
  if "parameters" in passed_parameter_dict:
302
321
  passed_parameter_dict = passed_parameter_dict["parameters"]
@@ -410,10 +429,9 @@ class LanguageModel(
410
429
  self,
411
430
  user_prompt: str,
412
431
  system_prompt: str,
413
- cache: Cache,
432
+ cache: "Cache",
414
433
  iteration: int = 0,
415
- files_list: Optional[List[FileStore]] = None,
416
- invigilator=None,
434
+ files_list=None,
417
435
  ) -> ModelResponse:
418
436
  """Handle caching of responses.
419
437
 
@@ -437,6 +455,7 @@ class LanguageModel(
437
455
 
438
456
  if files_list:
439
457
  files_hash = "+".join([str(hash(file)) for file in files_list])
458
+ # print(f"Files hash: {files_hash}")
440
459
  user_prompt_with_hashes = user_prompt + f" {files_hash}"
441
460
  else:
442
461
  user_prompt_with_hashes = user_prompt
@@ -462,7 +481,9 @@ class LanguageModel(
462
481
  "user_prompt": user_prompt,
463
482
  "system_prompt": system_prompt,
464
483
  "files_list": files_list,
484
+ # **({"encoded_image": encoded_image} if encoded_image else {}),
465
485
  }
486
+ # response = await f(**params)
466
487
  response = await asyncio.wait_for(f(**params), timeout=TIMEOUT)
467
488
  new_cache_key = cache.store(
468
489
  **cache_call_params, response=response
@@ -483,9 +504,11 @@ class LanguageModel(
483
504
  _async_get_intended_model_call_outcome
484
505
  )
485
506
 
507
+ # get_raw_response = sync_wrapper(async_get_raw_response)
508
+
486
509
  def simple_ask(
487
510
  self,
488
- question: QuestionBase,
511
+ question: "QuestionBase",
489
512
  system_prompt="You are a helpful agent pretending to be a human.",
490
513
  top_logprobs=2,
491
514
  ):
@@ -500,10 +523,9 @@ class LanguageModel(
500
523
  self,
501
524
  user_prompt: str,
502
525
  system_prompt: str,
503
- cache: Cache,
526
+ cache: "Cache",
504
527
  iteration: int = 1,
505
- files_list: Optional[List[FileStore]] = None,
506
- **kwargs,
528
+ files_list: Optional[List["File"]] = None,
507
529
  ) -> dict:
508
530
  """Get response, parse, and return as string.
509
531
 
@@ -521,9 +543,6 @@ class LanguageModel(
521
543
  "cache": cache,
522
544
  "files_list": files_list,
523
545
  }
524
- if "invigilator" in kwargs:
525
- params.update({"invigilator": kwargs["invigilator"]})
526
-
527
546
  model_inputs = ModelInputs(user_prompt=user_prompt, system_prompt=system_prompt)
528
547
  model_outputs = await self._async_get_intended_model_call_outcome(**params)
529
548
  edsl_dict = self.parse_response(model_outputs.response)
@@ -534,6 +553,8 @@ class LanguageModel(
534
553
  )
535
554
  return agent_response_dict
536
555
 
556
+ # return await self._async_prepare_response(model_call_outcome, cache=cache)
557
+
537
558
  get_response = sync_wrapper(async_get_response)
538
559
 
539
560
  def cost(self, raw_response: dict[str, Any]) -> Union[float, str]:
@@ -583,20 +604,21 @@ class LanguageModel(
583
604
 
584
605
  return input_cost + output_cost
585
606
 
586
- def to_dict(self, add_edsl_version: bool = True) -> dict[str, Any]:
587
- """Convert instance to a dictionary
607
+ #######################
608
+ # SERIALIZATION METHODS
609
+ #######################
610
+ def _to_dict(self) -> dict[str, Any]:
611
+ return {"model": self.model, "parameters": self.parameters}
612
+
613
+ @add_edsl_version
614
+ def to_dict(self) -> dict[str, Any]:
615
+ """Convert instance to a dictionary.
588
616
 
589
617
  >>> m = LanguageModel.example()
590
618
  >>> m.to_dict()
591
619
  {'model': '...', 'parameters': {'temperature': ..., 'max_tokens': ..., 'top_p': ..., 'frequency_penalty': ..., 'presence_penalty': ..., 'logprobs': False, 'top_logprobs': ...}, 'edsl_version': '...', 'edsl_class_name': 'LanguageModel'}
592
620
  """
593
- d = {"model": self.model, "parameters": self.parameters}
594
- if add_edsl_version:
595
- from edsl import __version__
596
-
597
- d["edsl_version"] = __version__
598
- d["edsl_class_name"] = self.__class__.__name__
599
- return d
621
+ return self._to_dict()
600
622
 
601
623
  @classmethod
602
624
  @remove_edsl_version
@@ -605,8 +627,18 @@ class LanguageModel(
605
627
  from edsl.language_models.registry import get_model_class
606
628
 
607
629
  model_class = get_model_class(data["model"])
630
+ # data["use_cache"] = True
608
631
  return model_class(**data)
609
632
 
633
+ #######################
634
+ # DUNDER METHODS
635
+ #######################
636
+ def print(self):
637
+ from rich import print_json
638
+ import json
639
+
640
+ print_json(json.dumps(self.to_dict()))
641
+
610
642
  def __repr__(self) -> str:
611
643
  """Return a string representation of the object."""
612
644
  param_string = ", ".join(
@@ -620,21 +652,33 @@ class LanguageModel(
620
652
 
621
653
  def __add__(self, other_model: Type[LanguageModel]) -> Type[LanguageModel]:
622
654
  """Combine two models into a single model (other_model takes precedence over self)."""
623
- import warnings
624
-
625
- warnings.warn(
655
+ print(
626
656
  f"""Warning: one model is replacing another. If you want to run both models, use a single `by` e.g.,
627
657
  by(m1, m2, m3) not by(m1).by(m2).by(m3)."""
628
658
  )
629
659
  return other_model or self
630
660
 
661
+ def rich_print(self):
662
+ """Display an object as a table."""
663
+ from rich.table import Table
664
+
665
+ table = Table(title="Language Model")
666
+ table.add_column("Attribute", style="bold")
667
+ table.add_column("Value")
668
+
669
+ to_display = self.__dict__.copy()
670
+ for attr_name, attr_value in to_display.items():
671
+ table.add_row(attr_name, repr(attr_value))
672
+
673
+ return table
674
+
631
675
  @classmethod
632
676
  def example(
633
677
  cls,
634
678
  test_model: bool = False,
635
679
  canned_response: str = "Hello world",
636
680
  throw_exception: bool = False,
637
- ) -> LanguageModel:
681
+ ):
638
682
  """Return a default instance of the class.
639
683
 
640
684
  >>> from edsl.language_models import LanguageModel
@@ -645,17 +689,11 @@ class LanguageModel(
645
689
  >>> q = QuestionFreeText(question_text = "What is your name?", question_name = 'example')
646
690
  >>> q.by(m).run(cache = False, disable_remote_cache = True, disable_remote_inference = True).select('example').first()
647
691
  'WOWZA!'
648
- >>> m = LanguageModel.example(test_model = True, canned_response = "WOWZA!", throw_exception = True)
649
- >>> r = q.by(m).run(cache = False, disable_remote_cache = True, disable_remote_inference = True, print_exceptions = True)
650
- Exception report saved to ...
651
- Also see: ...
652
692
  """
653
693
  from edsl import Model
654
694
 
655
695
  if test_model:
656
- m = Model(
657
- "test", canned_response=canned_response, throw_exception=throw_exception
658
- )
696
+ m = Model("test", canned_response=canned_response)
659
697
  return m
660
698
  else:
661
699
  return Model(skip_api_key_check=True)
@@ -1,4 +1,4 @@
1
- from typing import Optional, List
1
+ from typing import Optional
2
2
  from collections import UserList
3
3
  from edsl import Model
4
4
 
@@ -10,8 +10,6 @@ from edsl.utilities.utilities import dict_hash
10
10
 
11
11
 
12
12
  class ModelList(Base, UserList):
13
- __documentation__ = """https://docs.expectedparrot.com/en/latest/language_models.html#module-edsl.language_models.ModelList"""
14
-
15
13
  def __init__(self, data: Optional[list] = None):
16
14
  """Initialize the ScenarioList class.
17
15
 
@@ -39,9 +37,6 @@ class ModelList(Base, UserList):
39
37
  def __repr__(self):
40
38
  return f"ModelList({super().__repr__()})"
41
39
 
42
- def _summary(self):
43
- return {"EDSL Class": "ModelList", "Number of Models": len(self)}
44
-
45
40
  def __hash__(self):
46
41
  """Return a hash of the ModelList. This is used for comparison of ModelLists.
47
42
 
@@ -51,71 +46,14 @@ class ModelList(Base, UserList):
51
46
  """
52
47
  from edsl.utilities.utilities import dict_hash
53
48
 
54
- return dict_hash(self.to_dict(sort=True, add_edsl_version=False))
55
-
56
- def to_scenario_list(self):
57
- from edsl import ScenarioList, Scenario
58
-
59
- sl = ScenarioList()
60
- for model in self:
61
- d = {"model": model.model}
62
- d.update(model.parameters)
63
- sl.append(Scenario(d))
64
- return sl
65
-
66
- def tree(self, node_list: Optional[List[str]] = None):
67
- return self.to_scenario_list().tree(node_list)
68
-
69
- def table(
70
- self,
71
- *fields,
72
- tablefmt: Optional[str] = None,
73
- pretty_labels: Optional[dict] = None,
74
- ):
75
- """
76
- >>> ModelList.example().table("model")
77
- model
78
- -------
79
- gpt-4o
80
- gpt-4o
81
- gpt-4o
82
- """
83
- return (
84
- self.to_scenario_list()
85
- .to_dataset()
86
- .table(*fields, tablefmt=tablefmt, pretty_labels=pretty_labels)
87
- )
49
+ return dict_hash(self._to_dict(sort=True))
88
50
 
89
- def to_list(self):
90
- return self.to_scenario_list().to_list()
91
-
92
- def to_dict(self, sort=False, add_edsl_version=True):
51
+ def _to_dict(self, sort=False):
93
52
  if sort:
94
53
  model_list = sorted([model for model in self], key=lambda x: hash(x))
95
- d = {
96
- "models": [
97
- model.to_dict(add_edsl_version=add_edsl_version)
98
- for model in model_list
99
- ]
100
- }
54
+ return {"models": [model._to_dict() for model in model_list]}
101
55
  else:
102
- d = {
103
- "models": [
104
- model.to_dict(add_edsl_version=add_edsl_version) for model in self
105
- ]
106
- }
107
- if add_edsl_version:
108
- from edsl import __version__
109
-
110
- d["edsl_version"] = __version__
111
- d["edsl_class_name"] = "ModelList"
112
-
113
- return d
114
-
115
- def _repr_html_(self):
116
- """Return an HTML representation of the ModelList."""
117
- footer = f"<a href={self.__documentation__}>(docs)</a>"
118
- return str(self.summary(format="html")) + footer
56
+ return {"models": [model._to_dict() for model in self]}
119
57
 
120
58
  @classmethod
121
59
  def from_names(self, *args, **kwargs):
@@ -124,6 +62,15 @@ class ModelList(Base, UserList):
124
62
  args = args[0]
125
63
  return ModelList([Model(model_name, **kwargs) for model_name in args])
126
64
 
65
+ @add_edsl_version
66
+ def to_dict(self):
67
+ """
68
+ Convert the ModelList to a dictionary.
69
+ >>> ModelList.example().to_dict()
70
+ {'models': [...], 'edsl_version': '...', 'edsl_class_name': 'ModelList'}
71
+ """
72
+ return self._to_dict()
73
+
127
74
  @classmethod
128
75
  @remove_edsl_version
129
76
  def from_dict(cls, data):
@@ -7,49 +7,6 @@ from edsl.config import CONFIG
7
7
  # else:
8
8
  # default_model = CONFIG.get("EDSL_DEFAULT_MODEL")
9
9
 
10
- from collections import UserList
11
-
12
-
13
- class PrettyList(UserList):
14
- def __init__(self, data=None, columns=None):
15
- super().__init__(data)
16
- self.columns = columns
17
-
18
- def _repr_html_(self):
19
- if isinstance(self[0], list) or isinstance(self[0], tuple):
20
- num_cols = len(self[0])
21
- else:
22
- num_cols = 1
23
-
24
- if self.columns:
25
- columns = self.columns
26
- else:
27
- columns = list(range(num_cols))
28
-
29
- if num_cols > 1:
30
- return (
31
- "<pre><table>"
32
- + "".join(["<th>" + str(column) + "</th>" for column in columns])
33
- + "".join(
34
- [
35
- "<tr>"
36
- + "".join(["<td>" + str(x) + "</td>" for x in row])
37
- + "</tr>"
38
- for row in self
39
- ]
40
- )
41
- + "</table></pre>"
42
- )
43
- else:
44
- return (
45
- "<pre><table>"
46
- + "".join(["<th>" + str(index) + "</th>" for index in columns])
47
- + "".join(
48
- ["<tr>" + "<td>" + str(row) + "</td>" + "</tr>" for row in self]
49
- )
50
- + "</table></pre>"
51
- )
52
-
53
10
 
54
11
  def get_model_class(model_name, registry=None):
55
12
  from edsl.inference_services.registry import default
@@ -125,27 +82,17 @@ class Model(metaclass=Meta):
125
82
 
126
83
  if search_term is None:
127
84
  if name_only:
128
- return PrettyList(
129
- [m[0] for m in full_list],
130
- columns=["Model Name", "Service Name", "Code"],
131
- )
85
+ return [m[0] for m in full_list]
132
86
  else:
133
- return PrettyList(
134
- full_list, columns=["Model Name", "Service Name", "Code"]
135
- )
87
+ return full_list
136
88
  else:
137
89
  filtered_results = [
138
90
  m for m in full_list if search_term in m[0] or search_term in m[1]
139
91
  ]
140
92
  if name_only:
141
- return PrettyList(
142
- [m[0] for m in filtered_results],
143
- columns=["Model Name", "Service Name", "Code"],
144
- )
93
+ return [m[0] for m in filtered_results]
145
94
  else:
146
- return PrettyList(
147
- filtered_results, columns=["Model Name", "Service Name", "Code"]
148
- )
95
+ return filtered_results
149
96
 
150
97
  @classmethod
151
98
  def check_models(cls, verbose=False):
@@ -102,17 +102,18 @@ class Notebook(Base):
102
102
 
103
103
  return dict_hash(self.data["cells"])
104
104
 
105
- def to_dict(self, add_edsl_version=False) -> dict:
105
+ def _to_dict(self) -> dict:
106
106
  """
107
107
  Serialize to a dictionary.
108
108
  """
109
- d = {"name": self.name, "data": self.data}
110
- if add_edsl_version:
111
- from edsl import __version__
109
+ return {"name": self.name, "data": self.data}
112
110
 
113
- d["edsl_version"] = __version__
114
- d["edsl_class_name"] = self.__class__.__name__
115
- return d
111
+ @add_edsl_version
112
+ def to_dict(self) -> dict:
113
+ """
114
+ Convert a Notebook to a dictionary.
115
+ """
116
+ return self._to_dict()
116
117
 
117
118
  @classmethod
118
119
  @remove_edsl_version
edsl/prompts/Prompt.py CHANGED
@@ -29,14 +29,9 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
29
29
 
30
30
  def _repr_html_(self):
31
31
  """Return an HTML representation of the Prompt."""
32
- # from edsl.utilities.utilities import data_to_html
33
- # return data_to_html(self.to_dict())
34
- d = self.to_dict()
35
- data = [[k, v] for k, v in d.items()]
36
- from tabulate import tabulate
37
-
38
- table = str(tabulate(data, headers=["keys", "values"], tablefmt="html"))
39
- return f"<pre>{table}</pre>"
32
+ from edsl.utilities.utilities import data_to_html
33
+
34
+ return data_to_html(self.to_dict())
40
35
 
41
36
  def __len__(self):
42
37
  """Return the length of the prompt text."""
@@ -135,7 +135,7 @@ class QuestionBase(
135
135
  """
136
136
  from edsl.utilities.utilities import dict_hash
137
137
 
138
- return dict_hash(self.to_dict(add_edsl_version=False))
138
+ return dict_hash(self._to_dict())
139
139
 
140
140
  @property
141
141
  def data(self) -> dict:
@@ -147,15 +147,13 @@ class QuestionBase(
147
147
  """
148
148
  exclude_list = [
149
149
  "question_type",
150
- # "_include_comment",
150
+ "_include_comment",
151
151
  "_fake_data_factory",
152
- # "_use_code",
152
+ "_use_code",
153
153
  "_model_instructions",
154
154
  ]
155
155
  only_if_not_na_list = ["_answering_instructions", "_question_presentation"]
156
156
 
157
- only_if_not_default_list = {"_include_comment": True, "_use_code": False}
158
-
159
157
  def ok(key, value):
160
158
  if not key.startswith("_"):
161
159
  return False
@@ -163,12 +161,6 @@ class QuestionBase(
163
161
  return False
164
162
  if key in only_if_not_na_list and value is None:
165
163
  return False
166
- if (
167
- key in only_if_not_default_list
168
- and value == only_if_not_default_list[key]
169
- ):
170
- return False
171
-
172
164
  return True
173
165
 
174
166
  candidate_data = {
@@ -183,22 +175,25 @@ class QuestionBase(
183
175
 
184
176
  return candidate_data
185
177
 
186
- def to_dict(self, add_edsl_version=True):
178
+ def _to_dict(self):
187
179
  """Convert the question to a dictionary that includes the question type (used in deserialization).
188
180
 
189
- >>> from edsl import QuestionFreeText as Q; Q.example().to_dict(add_edsl_version = False)
181
+ >>> from edsl import QuestionFreeText as Q; Q.example()._to_dict()
190
182
  {'question_name': 'how_are_you', 'question_text': 'How are you?', 'question_type': 'free_text'}
191
183
  """
192
184
  candidate_data = self.data.copy()
193
185
  candidate_data["question_type"] = self.question_type
194
- d = {key: value for key, value in candidate_data.items() if value is not None}
195
- if add_edsl_version:
196
- from edsl import __version__
197
-
198
- d["edsl_version"] = __version__
199
- d["edsl_class_name"] = "QuestionBase"
186
+ return {
187
+ key: value for key, value in candidate_data.items() if value is not None
188
+ }
200
189
 
201
- return d
190
+ @add_edsl_version
191
+ def to_dict(self) -> dict[str, Any]:
192
+ """Convert the question to a dictionary that includes the question type (used in deserialization).
193
+ >>> from edsl import QuestionFreeText as Q; Q.example().to_dict()
194
+ {'question_name': 'how_are_you', 'question_text': 'How are you?', 'question_type': 'free_text', 'edsl_version': '...'}
195
+ """
196
+ return self._to_dict()
202
197
 
203
198
  @classmethod
204
199
  @remove_edsl_version
@@ -264,9 +259,12 @@ class QuestionBase(
264
259
  >>> m.execute_model_call("", "")
265
260
  {'message': [{'text': "Yo, what's up?"}], 'usage': {'prompt_tokens': 1, 'completion_tokens': 1}}
266
261
  >>> Q.run_example(show_answer = True, model = m, disable_remote_cache = True, disable_remote_inference = True)
267
- answer.how_are_you
268
- --------------------
269
- Yo, what's up?
262
+ ┏━━━━━━━━━━━━━━━━┓
263
+ ┃ answer ┃
264
+ .how_are_you ┃
265
+ ┡━━━━━━━━━━━━━━━━┩
266
+ │ Yo, what's up? │
267
+ └────────────────┘
270
268
  """
271
269
  if model is None:
272
270
  from edsl import Model
@@ -282,7 +280,7 @@ class QuestionBase(
282
280
  )
283
281
  )
284
282
  if show_answer:
285
- return results.select("answer.*").print()
283
+ results.select("answer.*").print()
286
284
  else:
287
285
  return results
288
286
 
@@ -360,22 +358,16 @@ class QuestionBase(
360
358
 
361
359
  # region: Magic methods
362
360
  def _repr_html_(self):
363
- # from edsl.utilities.utilities import data_to_html
364
-
365
- data = self.to_dict(add_edsl_version=False)
366
- # keys = list(data.keys())
367
- # values = list(data.values())
368
- from tabulate import tabulate
361
+ from edsl.utilities.utilities import data_to_html
369
362
 
370
- return tabulate(data.items(), headers=["keys", "values"], tablefmt="html")
371
-
372
- # try:
373
- # _ = data.pop("edsl_version")
374
- # _ = data.pop("edsl_class_name")
375
- # except KeyError:
376
- # print("Serialized question lacks edsl version, but is should have it.")
363
+ data = self.to_dict()
364
+ try:
365
+ _ = data.pop("edsl_version")
366
+ _ = data.pop("edsl_class_name")
367
+ except KeyError:
368
+ print("Serialized question lacks edsl version, but is should have it.")
377
369
 
378
- # return data_to_html(data)
370
+ return data_to_html(data)
379
371
 
380
372
  def __getitem__(self, key: str) -> Any:
381
373
  """Get an attribute of the question so it can be treated like a dictionary.