edsl 0.1.37__py3-none-any.whl → 0.1.37.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 (46) hide show
  1. edsl/__version__.py +1 -1
  2. edsl/agents/Agent.py +35 -86
  3. edsl/agents/AgentList.py +0 -5
  4. edsl/agents/InvigilatorBase.py +23 -2
  5. edsl/agents/PromptConstructor.py +105 -148
  6. edsl/agents/descriptors.py +4 -17
  7. edsl/conjure/AgentConstructionMixin.py +3 -11
  8. edsl/conversation/Conversation.py +14 -66
  9. edsl/coop/coop.py +14 -148
  10. edsl/data/Cache.py +1 -1
  11. edsl/exceptions/__init__.py +3 -7
  12. edsl/exceptions/agents.py +19 -17
  13. edsl/exceptions/results.py +8 -11
  14. edsl/exceptions/surveys.py +10 -13
  15. edsl/inference_services/AwsBedrock.py +2 -7
  16. edsl/inference_services/InferenceServicesCollection.py +9 -32
  17. edsl/jobs/Jobs.py +71 -306
  18. edsl/jobs/interviews/InterviewExceptionEntry.py +1 -5
  19. edsl/jobs/tasks/TaskHistory.py +0 -1
  20. edsl/language_models/LanguageModel.py +59 -47
  21. edsl/language_models/__init__.py +0 -1
  22. edsl/prompts/Prompt.py +4 -11
  23. edsl/questions/QuestionBase.py +13 -53
  24. edsl/questions/QuestionBasePromptsMixin.py +33 -1
  25. edsl/questions/QuestionFreeText.py +0 -1
  26. edsl/questions/QuestionFunctional.py +2 -2
  27. edsl/questions/descriptors.py +28 -23
  28. edsl/results/DatasetExportMixin.py +1 -25
  29. edsl/results/Result.py +1 -16
  30. edsl/results/Results.py +120 -31
  31. edsl/results/ResultsDBMixin.py +1 -1
  32. edsl/results/Selector.py +1 -18
  33. edsl/scenarios/Scenario.py +12 -48
  34. edsl/scenarios/ScenarioHtmlMixin.py +2 -7
  35. edsl/scenarios/ScenarioList.py +1 -12
  36. edsl/surveys/Rule.py +4 -10
  37. edsl/surveys/Survey.py +77 -100
  38. edsl/utilities/utilities.py +0 -18
  39. {edsl-0.1.37.dist-info → edsl-0.1.37.dev1.dist-info}/METADATA +1 -1
  40. {edsl-0.1.37.dist-info → edsl-0.1.37.dev1.dist-info}/RECORD +42 -46
  41. edsl/conversation/chips.py +0 -95
  42. edsl/exceptions/BaseException.py +0 -21
  43. edsl/exceptions/scenarios.py +0 -22
  44. edsl/language_models/KeyLookup.py +0 -30
  45. {edsl-0.1.37.dist-info → edsl-0.1.37.dev1.dist-info}/LICENSE +0 -0
  46. {edsl-0.1.37.dist-info → edsl-0.1.37.dev1.dist-info}/WHEEL +0 -0
@@ -17,7 +17,9 @@ import warnings
17
17
  from functools import wraps
18
18
  import asyncio
19
19
  import json
20
+ import time
20
21
  import os
22
+ import hashlib
21
23
  from typing import (
22
24
  Coroutine,
23
25
  Any,
@@ -28,7 +30,6 @@ from typing import (
28
30
  get_type_hints,
29
31
  TypedDict,
30
32
  Optional,
31
- TYPE_CHECKING,
32
33
  )
33
34
  from abc import ABC, abstractmethod
34
35
 
@@ -48,16 +49,34 @@ from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
48
49
  from edsl.language_models.repair import repair
49
50
  from edsl.enums import InferenceServiceType
50
51
  from edsl.Base import RichPrintingMixin, PersistenceMixin
52
+ from edsl.enums import service_to_api_keyname
53
+ from edsl.exceptions import MissingAPIKeyError
51
54
  from edsl.language_models.RegisterLanguageModelsMeta import RegisterLanguageModelsMeta
52
55
  from edsl.exceptions.language_models import LanguageModelBadResponseError
53
56
 
54
- from edsl.language_models.KeyLookup import KeyLookup
55
-
56
57
  TIMEOUT = float(CONFIG.get("EDSL_API_TIMEOUT"))
57
58
 
58
59
 
59
- # you might be tempated to move this to be a static method of LanguageModel, but this doesn't work
60
- # for reasons I don't understand. So leave it here.
60
+ def convert_answer(response_part):
61
+ import json
62
+
63
+ response_part = response_part.strip()
64
+
65
+ if response_part == "None":
66
+ return None
67
+
68
+ repaired = repair_json(response_part)
69
+ if repaired == '""':
70
+ # it was a literal string
71
+ return response_part
72
+
73
+ try:
74
+ return json.loads(repaired)
75
+ except json.JSONDecodeError as j:
76
+ # last resort
77
+ return response_part
78
+
79
+
61
80
  def extract_item_from_raw_response(data, key_sequence):
62
81
  if isinstance(data, str):
63
82
  try:
@@ -148,12 +167,7 @@ class LanguageModel(
148
167
  _safety_factor = 0.8
149
168
 
150
169
  def __init__(
151
- self,
152
- tpm: float = None,
153
- rpm: float = None,
154
- omit_system_prompt_if_empty_string: bool = True,
155
- key_lookup: Optional[KeyLookup] = None,
156
- **kwargs,
170
+ self, tpm=None, rpm=None, omit_system_prompt_if_empty_string=True, **kwargs
157
171
  ):
158
172
  """Initialize the LanguageModel."""
159
173
  self.model = getattr(self, "_model_", None)
@@ -186,26 +200,29 @@ class LanguageModel(
186
200
  # Skip the API key check. Sometimes this is useful for testing.
187
201
  self._api_token = None
188
202
 
189
- if key_lookup is not None:
190
- self.key_lookup = key_lookup
191
- else:
192
- self.key_lookup = KeyLookup.from_os_environ()
193
-
194
203
  def ask_question(self, question):
195
204
  user_prompt = question.get_instructions().render(question.data).text
196
205
  system_prompt = "You are a helpful agent pretending to be a human."
197
206
  return self.execute_model_call(user_prompt, system_prompt)
198
207
 
199
- def set_key_lookup(self, key_lookup: KeyLookup):
200
- del self._api_token
201
- self.key_lookup = key_lookup
202
-
203
208
  @property
204
209
  def api_token(self) -> str:
205
210
  if not hasattr(self, "_api_token"):
206
- self._api_token = self.key_lookup.get_api_token(
207
- self._inference_service_, self.remote
208
- )
211
+ key_name = service_to_api_keyname.get(self._inference_service_, "NOT FOUND")
212
+ if self._inference_service_ == "bedrock":
213
+ self._api_token = [os.getenv(key_name[0]), os.getenv(key_name[1])]
214
+ # Check if any of the tokens are None
215
+ missing_token = any(token is None for token in self._api_token)
216
+ else:
217
+ self._api_token = os.getenv(key_name)
218
+ missing_token = self._api_token is None
219
+ if missing_token and self._inference_service_ != "test" and not self.remote:
220
+ print("raising error")
221
+ raise MissingAPIKeyError(
222
+ f"""The key for service: `{self._inference_service_}` is not set.
223
+ Need a key with name {key_name} in your .env file."""
224
+ )
225
+
209
226
  return self._api_token
210
227
 
211
228
  def __getitem__(self, key):
@@ -274,6 +291,21 @@ class LanguageModel(
274
291
  if tpm is not None:
275
292
  self._tpm = tpm
276
293
  return None
294
+ # self._set_rate_limits(rpm=rpm, tpm=tpm)
295
+
296
+ # def _set_rate_limits(self, rpm=None, tpm=None) -> None:
297
+ # """Set the rate limits for the model.
298
+
299
+ # If the model does not have rate limits, use the default rate limits."""
300
+ # if rpm is not None and tpm is not None:
301
+ # self.__rate_limits = {"rpm": rpm, "tpm": tpm}
302
+ # return
303
+
304
+ # if self.__rate_limits is None:
305
+ # if hasattr(self, "get_rate_limits"):
306
+ # self.__rate_limits = self.get_rate_limits()
307
+ # else:
308
+ # self.__rate_limits = self.__default_rate_limits
277
309
 
278
310
  @property
279
311
  def RPM(self):
@@ -384,26 +416,6 @@ class LanguageModel(
384
416
  )
385
417
  return extract_item_from_raw_response(raw_response, cls.usage_sequence)
386
418
 
387
- @staticmethod
388
- def convert_answer(response_part):
389
- import json
390
-
391
- response_part = response_part.strip()
392
-
393
- if response_part == "None":
394
- return None
395
-
396
- repaired = repair_json(response_part)
397
- if repaired == '""':
398
- # it was a literal string
399
- return response_part
400
-
401
- try:
402
- return json.loads(repaired)
403
- except json.JSONDecodeError as j:
404
- # last resort
405
- return response_part
406
-
407
419
  @classmethod
408
420
  def parse_response(cls, raw_response: dict[str, Any]) -> EDSLOutput:
409
421
  """Parses the API response and returns the response text."""
@@ -413,13 +425,13 @@ class LanguageModel(
413
425
  if last_newline == -1:
414
426
  # There is no comment
415
427
  edsl_dict = {
416
- "answer": cls.convert_answer(generated_token_string),
428
+ "answer": convert_answer(generated_token_string),
417
429
  "generated_tokens": generated_token_string,
418
430
  "comment": None,
419
431
  }
420
432
  else:
421
433
  edsl_dict = {
422
- "answer": cls.convert_answer(generated_token_string[:last_newline]),
434
+ "answer": convert_answer(generated_token_string[:last_newline]),
423
435
  "comment": generated_token_string[last_newline + 1 :].strip(),
424
436
  "generated_tokens": generated_token_string,
425
437
  }
@@ -480,7 +492,7 @@ class LanguageModel(
480
492
  params = {
481
493
  "user_prompt": user_prompt,
482
494
  "system_prompt": system_prompt,
483
- "files_list": files_list,
495
+ "files_list": files_list
484
496
  # **({"encoded_image": encoded_image} if encoded_image else {}),
485
497
  }
486
498
  # response = await f(**params)
@@ -687,7 +699,7 @@ class LanguageModel(
687
699
  True
688
700
  >>> from edsl import QuestionFreeText
689
701
  >>> q = QuestionFreeText(question_text = "What is your name?", question_name = 'example')
690
- >>> q.by(m).run(cache = False, disable_remote_cache = True, disable_remote_inference = True).select('example').first()
702
+ >>> q.by(m).run(cache = False).select('example').first()
691
703
  'WOWZA!'
692
704
  """
693
705
  from edsl import Model
@@ -1,3 +1,2 @@
1
1
  from edsl.language_models.LanguageModel import LanguageModel
2
2
  from edsl.language_models.registry import Model
3
- from edsl.language_models.KeyLookup import KeyLookup
edsl/prompts/Prompt.py CHANGED
@@ -52,9 +52,6 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
52
52
  text = self.default_instructions
53
53
  else:
54
54
  text = ""
55
- if isinstance(text, Prompt):
56
- # make it idempotent w/ a prompt
57
- text = text.text
58
55
  self._text = text
59
56
 
60
57
  @classmethod
@@ -240,14 +237,10 @@ class Prompt(PersistenceMixin, RichPrintingMixin):
240
237
  >>> p.render({"person": "Mr. {{last_name}}"})
241
238
  Prompt(text=\"""Hello, Mr. {{ last_name }}\""")
242
239
  """
243
- try:
244
- new_text = self._render(
245
- self.text, primary_replacement, **additional_replacements
246
- )
247
- return self.__class__(text=new_text)
248
- except Exception as e:
249
- print(f"Error rendering prompt: {e}")
250
- return self
240
+ new_text = self._render(
241
+ self.text, primary_replacement, **additional_replacements
242
+ )
243
+ return self.__class__(text=new_text)
251
244
 
252
245
  @staticmethod
253
246
  def _render(
@@ -150,21 +150,14 @@ class QuestionBase(
150
150
  "_include_comment",
151
151
  "_fake_data_factory",
152
152
  "_use_code",
153
+ "_answering_instructions",
154
+ "_question_presentation",
153
155
  "_model_instructions",
154
156
  ]
155
- only_if_not_na_list = ["_answering_instructions", "_question_presentation"]
156
-
157
- def ok(key, value):
158
- if not key.startswith("_"):
159
- return False
160
- if key in exclude_list:
161
- return False
162
- if key in only_if_not_na_list and value is None:
163
- return False
164
- return True
165
-
166
157
  candidate_data = {
167
- k.replace("_", "", 1): v for k, v in self.__dict__.items() if ok(k, v)
158
+ k.replace("_", "", 1): v
159
+ for k, v in self.__dict__.items()
160
+ if k.startswith("_") and k not in exclude_list
168
161
  }
169
162
 
170
163
  if "func" in candidate_data:
@@ -183,9 +176,7 @@ class QuestionBase(
183
176
  """
184
177
  candidate_data = self.data.copy()
185
178
  candidate_data["question_type"] = self.question_type
186
- return {
187
- key: value for key, value in candidate_data.items() if value is not None
188
- }
179
+ return candidate_data
189
180
 
190
181
  @add_edsl_version
191
182
  def to_dict(self) -> dict[str, Any]:
@@ -248,8 +239,6 @@ class QuestionBase(
248
239
  show_answer: bool = True,
249
240
  model: Optional["LanguageModel"] = None,
250
241
  cache=False,
251
- disable_remote_cache: bool = False,
252
- disable_remote_inference: bool = False,
253
242
  **kwargs,
254
243
  ):
255
244
  """Run an example of the question.
@@ -258,7 +247,7 @@ class QuestionBase(
258
247
  >>> m = Q._get_test_model(canned_response = "Yo, what's up?")
259
248
  >>> m.execute_model_call("", "")
260
249
  {'message': [{'text': "Yo, what's up?"}], 'usage': {'prompt_tokens': 1, 'completion_tokens': 1}}
261
- >>> Q.run_example(show_answer = True, model = m, disable_remote_cache = True, disable_remote_inference = True)
250
+ >>> Q.run_example(show_answer = True, model = m)
262
251
  ┏━━━━━━━━━━━━━━━━┓
263
252
  ┃ answer ┃
264
253
  ┃ .how_are_you ┃
@@ -270,48 +259,25 @@ class QuestionBase(
270
259
  from edsl import Model
271
260
 
272
261
  model = Model()
273
- results = (
274
- cls.example(**kwargs)
275
- .by(model)
276
- .run(
277
- cache=cache,
278
- disable_remote_cache=disable_remote_cache,
279
- disable_remote_inference=disable_remote_inference,
280
- )
281
- )
262
+ results = cls.example(**kwargs).by(model).run(cache=cache)
282
263
  if show_answer:
283
264
  results.select("answer.*").print()
284
265
  else:
285
266
  return results
286
267
 
287
- def __call__(
288
- self,
289
- just_answer=True,
290
- model=None,
291
- agent=None,
292
- disable_remote_cache: bool = False,
293
- disable_remote_inference: bool = False,
294
- **kwargs,
295
- ):
268
+ def __call__(self, just_answer=True, model=None, agent=None, **kwargs):
296
269
  """Call the question.
297
270
 
298
271
 
299
272
  >>> from edsl import QuestionFreeText as Q
300
273
  >>> m = Q._get_test_model(canned_response = "Yo, what's up?")
301
274
  >>> q = Q(question_name = "color", question_text = "What is your favorite color?")
302
- >>> q(model = m, disable_remote_cache = True, disable_remote_inference = True)
275
+ >>> q(model = m)
303
276
  "Yo, what's up?"
304
277
 
305
278
  """
306
279
  survey = self.to_survey()
307
- results = survey(
308
- model=model,
309
- agent=agent,
310
- **kwargs,
311
- cache=False,
312
- disable_remote_cache=disable_remote_cache,
313
- disable_remote_inference=disable_remote_inference,
314
- )
280
+ results = survey(model=model, agent=agent, **kwargs, cache=False)
315
281
  if just_answer:
316
282
  return results.select(f"answer.{self.question_name}").first()
317
283
  else:
@@ -329,7 +295,6 @@ class QuestionBase(
329
295
  just_answer: bool = True,
330
296
  model: Optional["Model"] = None,
331
297
  agent: Optional["Agent"] = None,
332
- disable_remote_inference: bool = False,
333
298
  **kwargs,
334
299
  ) -> Union[Any, "Results"]:
335
300
  """Call the question asynchronously.
@@ -338,17 +303,12 @@ class QuestionBase(
338
303
  >>> from edsl import QuestionFreeText as Q
339
304
  >>> m = Q._get_test_model(canned_response = "Blue")
340
305
  >>> q = Q(question_name = "color", question_text = "What is your favorite color?")
341
- >>> async def test_run_async(): result = await q.run_async(model=m, disable_remote_inference = True); print(result)
306
+ >>> async def test_run_async(): result = await q.run_async(model=m); print(result)
342
307
  >>> asyncio.run(test_run_async())
343
308
  Blue
344
309
  """
345
310
  survey = self.to_survey()
346
- results = await survey.run_async(
347
- model=model,
348
- agent=agent,
349
- disable_remote_inference=disable_remote_inference,
350
- **kwargs,
351
- )
311
+ results = await survey.run_async(model=model, agent=agent, **kwargs)
352
312
  if just_answer:
353
313
  return results.select(f"answer.{self.question_name}").first()
354
314
  else:
@@ -30,6 +30,38 @@ template_manager = TemplateManager()
30
30
 
31
31
 
32
32
  class QuestionBasePromptsMixin:
33
+ # @classmethod
34
+ # @lru_cache(maxsize=1)
35
+ # def _read_template(cls, template_name):
36
+ # with resources.open_text(
37
+ # f"edsl.questions.templates.{cls.question_type}", template_name
38
+ # ) as file:
39
+ # return file.read()
40
+
41
+ # @classmethod
42
+ # def applicable_prompts(
43
+ # cls, model: Optional[str] = None
44
+ # ) -> list[type["PromptBase"]]:
45
+ # """Get the prompts that are applicable to the question type.
46
+
47
+ # :param model: The language model to use.
48
+
49
+ # >>> from edsl.questions import QuestionFreeText
50
+ # >>> QuestionFreeText.applicable_prompts()
51
+ # [<class 'edsl.prompts.library.question_freetext.FreeText'>]
52
+
53
+ # :param model: The language model to use. If None, assumes does not matter.
54
+
55
+ # """
56
+ # from edsl.prompts.registry import get_classes as prompt_lookup
57
+
58
+ # applicable_prompts = prompt_lookup(
59
+ # component_type="question_instructions",
60
+ # question_type=cls.question_type,
61
+ # model=model,
62
+ # )
63
+ # return applicable_prompts
64
+
33
65
  @property
34
66
  def model_instructions(self) -> dict:
35
67
  """Get the model-specific instructions for the question."""
@@ -199,7 +231,7 @@ class QuestionBasePromptsMixin:
199
231
  @property
200
232
  def new_default_instructions(self) -> "Prompt":
201
233
  "This is set up as a property because there are mutable question values that determine how it is rendered."
202
- return Prompt(self.question_presentation) + Prompt(self.answering_instructions)
234
+ return self.question_presentation + self.answering_instructions
203
235
 
204
236
  @property
205
237
  def parameters(self) -> set[str]:
@@ -14,7 +14,6 @@ from pydantic import BaseModel
14
14
  from typing import Optional, Any, List
15
15
 
16
16
  from edsl.exceptions import QuestionAnswerValidationError
17
- from edsl.prompts.Prompt import Prompt
18
17
 
19
18
 
20
19
  class FreeTextResponse(BaseModel):
@@ -19,7 +19,7 @@ class QuestionFunctional(QuestionBase):
19
19
  >>> question.activate()
20
20
  >>> scenario = Scenario({"numbers": [1, 2, 3, 4, 5]})
21
21
  >>> agent = Agent(traits={"multiplier": 10})
22
- >>> results = question.by(scenario).by(agent).run(disable_remote_cache = True, disable_remote_inference = True)
22
+ >>> results = question.by(scenario).by(agent).run()
23
23
  >>> results.select("answer.*").to_list()[0] == 150
24
24
  True
25
25
 
@@ -27,7 +27,7 @@ class QuestionFunctional(QuestionBase):
27
27
 
28
28
  >>> from edsl.questions.QuestionBase import QuestionBase
29
29
  >>> new_question = QuestionBase.from_dict(question.to_dict())
30
- >>> results = new_question.by(scenario).by(agent).run(disable_remote_cache = True, disable_remote_inference = True)
30
+ >>> results = new_question.by(scenario).by(agent).run()
31
31
  >>> results.select("answer.*").to_list()[0] == 150
32
32
  True
33
33
 
@@ -53,12 +53,33 @@ class BaseDescriptor(ABC):
53
53
 
54
54
  def __set__(self, instance, value: Any) -> None:
55
55
  """Set the value of the attribute."""
56
- new_value = self.validate(value, instance)
57
-
58
- if new_value is not None:
59
- instance.__dict__[self.name] = new_value
60
- else:
61
- instance.__dict__[self.name] = value
56
+ self.validate(value, instance)
57
+ # from edsl.prompts.registry import get_classes
58
+
59
+ instance.__dict__[self.name] = value
60
+ # if self.name == "_instructions":
61
+ # instructions = value
62
+ # if value is not None:
63
+ # instance.__dict__[self.name] = instructions
64
+ # instance.set_instructions = True
65
+ # else:
66
+ # potential_prompt_classes = get_classes(
67
+ # question_type=instance.question_type
68
+ # )
69
+ # if len(potential_prompt_classes) > 0:
70
+ # instructions = potential_prompt_classes[0]().text
71
+ # instance.__dict__[self.name] = instructions
72
+ # instance.set_instructions = False
73
+ # else:
74
+ # if not hasattr(instance, "default_instructions"):
75
+ # raise Exception(
76
+ # "No default instructions found and no matching prompts!"
77
+ # )
78
+ # instructions = instance.default_instructions
79
+ # instance.__dict__[self.name] = instructions
80
+ # instance.set_instructions = False
81
+
82
+ # instance.set_instructions = value != instance.default_instructions
62
83
 
63
84
  def __set_name__(self, owner, name: str) -> None:
64
85
  """Set the name of the attribute."""
@@ -379,24 +400,10 @@ class QuestionTextDescriptor(BaseDescriptor):
379
400
  if contains_single_braced_substring(value):
380
401
  import warnings
381
402
 
382
- # # warnings.warn(
383
- # # f"WARNING: Question text contains a single-braced substring: If you intended to parameterize the question with a Scenario this should be changed to a double-braced substring, e.g. {{variable}}.\nSee details on constructing Scenarios in the docs: https://docs.expectedparrot.com/en/latest/scenarios.html",
384
- # # UserWarning,
385
- # # )
386
403
  warnings.warn(
387
- "WARNING: Question text contains a single-braced substring. "
388
- "If you intended to parameterize the question with a Scenario, this will "
389
- "be changed to a double-braced substring, e.g. {{variable}}.\n"
390
- "See details on constructing Scenarios in the docs: "
391
- "https://docs.expectedparrot.com/en/latest/scenarios.html",
404
+ f"WARNING: Question text contains a single-braced substring: If you intended to parameterize the question with a Scenario this should be changed to a double-braced substring, e.g. {{variable}}.\nSee details on constructing Scenarios in the docs: https://docs.expectedparrot.com/en/latest/scenarios.html",
392
405
  UserWarning,
393
406
  )
394
- # Automatically replace single braces with double braces
395
- # This is here because if the user is using an f-string, the double brace will get converted to a single brace.
396
- # This undoes that.
397
- value = re.sub(r"\{([^\{\}]+)\}", r"{{\1}}", value)
398
- return value
399
-
400
407
  # iterate through all doubles braces and check if they are valid python identifiers
401
408
  for match in re.finditer(r"\{\{([^\{\}]+)\}\}", value):
402
409
  if " " in match.group(1).strip():
@@ -404,8 +411,6 @@ class QuestionTextDescriptor(BaseDescriptor):
404
411
  f"Question text contains an invalid identifier: '{match.group(1)}'"
405
412
  )
406
413
 
407
- return None
408
-
409
414
 
410
415
  if __name__ == "__main__":
411
416
  import doctest
@@ -437,30 +437,7 @@ class DatasetExportMixin:
437
437
  b64 = base64.b64encode(csv_string.encode()).decode()
438
438
  return f'<a href="data:file/csv;base64,{b64}" download="my_data.csv">Download CSV file</a>'
439
439
 
440
- def to_pandas(
441
- self, remove_prefix: bool = False, lists_as_strings=False
442
- ) -> "DataFrame":
443
- """Convert the results to a pandas DataFrame, ensuring that lists remain as lists.
444
-
445
- :param remove_prefix: Whether to remove the prefix from the column names.
446
-
447
- """
448
- return self._to_pandas_strings(remove_prefix)
449
- # if lists_as_strings:
450
- # return self._to_pandas_strings(remove_prefix=remove_prefix)
451
-
452
- # import pandas as pd
453
-
454
- # df = pd.DataFrame(self.data)
455
-
456
- # if remove_prefix:
457
- # # Optionally remove prefixes from column names
458
- # df.columns = [col.split(".")[-1] for col in df.columns]
459
-
460
- # df_sorted = df.sort_index(axis=1) # Sort columns alphabetically
461
- # return df_sorted
462
-
463
- def _to_pandas_strings(self, remove_prefix: bool = False) -> "pd.DataFrame":
440
+ def to_pandas(self, remove_prefix: bool = False) -> "pd.DataFrame":
464
441
  """Convert the results to a pandas DataFrame.
465
442
 
466
443
  :param remove_prefix: Whether to remove the prefix from the column names.
@@ -474,7 +451,6 @@ class DatasetExportMixin:
474
451
  2 Terrible
475
452
  3 OK
476
453
  """
477
-
478
454
  import pandas as pd
479
455
 
480
456
  csv_string = self.to_csv(remove_prefix=remove_prefix)
edsl/results/Result.py CHANGED
@@ -257,25 +257,10 @@ class Result(Base, UserDict):
257
257
 
258
258
  """
259
259
  d = {}
260
- problem_keys = []
261
- data_types = sorted(self.sub_dicts.keys())
260
+ data_types = self.sub_dicts.keys()
262
261
  for data_type in data_types:
263
262
  for key in self.sub_dicts[data_type]:
264
- if key in d:
265
- import warnings
266
-
267
- warnings.warn(
268
- f"Key '{key}' of data type '{data_type}' is already in use. Renaming to {key}_{data_type}"
269
- )
270
- problem_keys.append((key, data_type))
271
- key = f"{key}_{data_type}"
272
- # raise ValueError(f"Key '{key}' is already in the dictionary")
273
263
  d[key] = data_type
274
-
275
- for key, data_type in problem_keys:
276
- self.sub_dicts[data_type][f"{key}_{data_type}"] = self.sub_dicts[
277
- data_type
278
- ].pop(key)
279
264
  return d
280
265
 
281
266
  def rows(self, index) -> tuple[int, str, str, str]: