edsl 0.1.54__py3-none-any.whl → 0.1.55__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 (101) hide show
  1. edsl/__init__.py +8 -1
  2. edsl/__init__original.py +134 -0
  3. edsl/__version__.py +1 -1
  4. edsl/agents/agent.py +29 -0
  5. edsl/agents/agent_list.py +36 -1
  6. edsl/base/base_class.py +281 -151
  7. edsl/buckets/__init__.py +8 -3
  8. edsl/buckets/bucket_collection.py +9 -3
  9. edsl/buckets/model_buckets.py +4 -2
  10. edsl/buckets/token_bucket.py +2 -2
  11. edsl/buckets/token_bucket_client.py +5 -3
  12. edsl/caching/cache.py +131 -62
  13. edsl/caching/cache_entry.py +70 -58
  14. edsl/caching/sql_dict.py +17 -0
  15. edsl/cli.py +99 -0
  16. edsl/config/config_class.py +16 -0
  17. edsl/conversation/__init__.py +31 -0
  18. edsl/coop/coop.py +276 -242
  19. edsl/coop/coop_jobs_objects.py +59 -0
  20. edsl/coop/coop_objects.py +29 -0
  21. edsl/coop/coop_regular_objects.py +26 -0
  22. edsl/coop/utils.py +24 -19
  23. edsl/dataset/dataset.py +338 -101
  24. edsl/db_list/sqlite_list.py +349 -0
  25. edsl/inference_services/__init__.py +40 -5
  26. edsl/inference_services/exceptions.py +11 -0
  27. edsl/inference_services/services/anthropic_service.py +5 -2
  28. edsl/inference_services/services/aws_bedrock.py +6 -2
  29. edsl/inference_services/services/azure_ai.py +6 -2
  30. edsl/inference_services/services/google_service.py +3 -2
  31. edsl/inference_services/services/mistral_ai_service.py +6 -2
  32. edsl/inference_services/services/open_ai_service.py +6 -2
  33. edsl/inference_services/services/perplexity_service.py +6 -2
  34. edsl/inference_services/services/test_service.py +94 -5
  35. edsl/interviews/answering_function.py +167 -59
  36. edsl/interviews/interview.py +124 -72
  37. edsl/interviews/interview_task_manager.py +10 -0
  38. edsl/invigilators/invigilators.py +9 -0
  39. edsl/jobs/async_interview_runner.py +146 -104
  40. edsl/jobs/data_structures.py +6 -4
  41. edsl/jobs/decorators.py +61 -0
  42. edsl/jobs/fetch_invigilator.py +61 -18
  43. edsl/jobs/html_table_job_logger.py +14 -2
  44. edsl/jobs/jobs.py +180 -104
  45. edsl/jobs/jobs_component_constructor.py +2 -2
  46. edsl/jobs/jobs_interview_constructor.py +2 -0
  47. edsl/jobs/jobs_remote_inference_logger.py +4 -0
  48. edsl/jobs/jobs_runner_status.py +30 -25
  49. edsl/jobs/progress_bar_manager.py +79 -0
  50. edsl/jobs/remote_inference.py +35 -1
  51. edsl/key_management/key_lookup_builder.py +6 -1
  52. edsl/language_models/language_model.py +86 -6
  53. edsl/language_models/model.py +10 -3
  54. edsl/language_models/price_manager.py +45 -75
  55. edsl/language_models/registry.py +5 -0
  56. edsl/notebooks/notebook.py +77 -10
  57. edsl/questions/VALIDATION_README.md +134 -0
  58. edsl/questions/__init__.py +24 -1
  59. edsl/questions/exceptions.py +21 -0
  60. edsl/questions/question_dict.py +201 -16
  61. edsl/questions/question_multiple_choice_with_other.py +624 -0
  62. edsl/questions/question_registry.py +2 -1
  63. edsl/questions/templates/multiple_choice_with_other/__init__.py +0 -0
  64. edsl/questions/templates/multiple_choice_with_other/answering_instructions.jinja +15 -0
  65. edsl/questions/templates/multiple_choice_with_other/question_presentation.jinja +17 -0
  66. edsl/questions/validation_analysis.py +185 -0
  67. edsl/questions/validation_cli.py +131 -0
  68. edsl/questions/validation_html_report.py +404 -0
  69. edsl/questions/validation_logger.py +136 -0
  70. edsl/results/result.py +63 -16
  71. edsl/results/results.py +702 -171
  72. edsl/scenarios/construct_download_link.py +16 -3
  73. edsl/scenarios/directory_scanner.py +226 -226
  74. edsl/scenarios/file_methods.py +5 -0
  75. edsl/scenarios/file_store.py +117 -6
  76. edsl/scenarios/handlers/__init__.py +5 -1
  77. edsl/scenarios/handlers/mp4_file_store.py +104 -0
  78. edsl/scenarios/handlers/webm_file_store.py +104 -0
  79. edsl/scenarios/scenario.py +120 -101
  80. edsl/scenarios/scenario_list.py +800 -727
  81. edsl/scenarios/scenario_list_gc_test.py +146 -0
  82. edsl/scenarios/scenario_list_memory_test.py +214 -0
  83. edsl/scenarios/scenario_list_source_refactor.md +35 -0
  84. edsl/scenarios/scenario_selector.py +5 -4
  85. edsl/scenarios/scenario_source.py +1990 -0
  86. edsl/scenarios/tests/test_scenario_list_sources.py +52 -0
  87. edsl/surveys/survey.py +22 -0
  88. edsl/tasks/__init__.py +4 -2
  89. edsl/tasks/task_history.py +198 -36
  90. edsl/tests/scenarios/test_ScenarioSource.py +51 -0
  91. edsl/tests/scenarios/test_scenario_list_sources.py +51 -0
  92. edsl/utilities/__init__.py +2 -1
  93. edsl/utilities/decorators.py +121 -0
  94. edsl/utilities/memory_debugger.py +1010 -0
  95. {edsl-0.1.54.dist-info → edsl-0.1.55.dist-info}/METADATA +51 -76
  96. {edsl-0.1.54.dist-info → edsl-0.1.55.dist-info}/RECORD +99 -75
  97. edsl/jobs/jobs_runner_asyncio.py +0 -281
  98. edsl/language_models/unused/fake_openai_service.py +0 -60
  99. {edsl-0.1.54.dist-info → edsl-0.1.55.dist-info}/LICENSE +0 -0
  100. {edsl-0.1.54.dist-info → edsl-0.1.55.dist-info}/WHEEL +0 -0
  101. {edsl-0.1.54.dist-info → edsl-0.1.55.dist-info}/entry_points.txt +0 -0
edsl/__init__.py CHANGED
@@ -1,3 +1,9 @@
1
+ """
2
+ EDSL: Experimental Design Specification Language
3
+
4
+ EDSL is a Python library for conducting virtual social science experiments, surveys,
5
+ and interviews with large language models.
6
+ """
1
7
  import os
2
8
  import time
3
9
  import importlib
@@ -15,7 +21,7 @@ from edsl import logger
15
21
  # Set up logger with configuration from environment/config
16
22
  # (We'll configure the logger after CONFIG is initialized below)
17
23
 
18
- __all__ = ["logger"]
24
+ __all__ = ["logger", "Config", "CONFIG", "__version__"]
19
25
 
20
26
  # Define modules to import
21
27
  modules_to_import = [
@@ -31,6 +37,7 @@ modules_to_import = [
31
37
  "coop",
32
38
  "instructions",
33
39
  "jobs",
40
+ "base",
34
41
  "conversation",
35
42
  ]
36
43
 
@@ -0,0 +1,134 @@
1
+ import os
2
+ import time
3
+ import importlib
4
+ import pkgutil
5
+
6
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
7
+ ROOT_DIR = os.path.dirname(BASE_DIR)
8
+
9
+ from edsl.__version__ import __version__
10
+ from edsl.config import Config, CONFIG
11
+
12
+ # Initialize and expose logger
13
+ from edsl import logger
14
+
15
+ # Set up logger with configuration from environment/config
16
+ # (We'll configure the logger after CONFIG is initialized below)
17
+
18
+ __all__ = ['logger']
19
+
20
+ # Define modules to import
21
+ modules_to_import = [
22
+ 'dataset',
23
+ 'agents',
24
+ 'surveys',
25
+ 'questions',
26
+ 'scenarios',
27
+ 'language_models',
28
+ 'results',
29
+ 'caching',
30
+ 'notebooks',
31
+ 'coop',
32
+ 'instructions',
33
+ 'jobs'
34
+ ]
35
+
36
+ # Dynamically import modules and extend __all__
37
+ for module_name in modules_to_import:
38
+ try:
39
+ # Import the module
40
+ module = importlib.import_module(f'.{module_name}', package='edsl')
41
+
42
+ # Get the module's __all__ attribute
43
+ module_all = getattr(module, '__all__', [])
44
+
45
+ # Import all names from the module
46
+ exec(f"from .{module_name} import *")
47
+
48
+ # Extend __all__ with the module's __all__
49
+ if module_all:
50
+ logger.debug(f"Adding {len(module_all)} items from {module_name} to __all__")
51
+ __all__.extend(module_all)
52
+ else:
53
+ logger.warning(f"Module {module_name} does not have __all__ defined")
54
+ except ImportError as e:
55
+ logger.warning(f"Failed to import module {module_name}: {e}")
56
+ except Exception as e:
57
+ logger.warning(f"Error importing from module {module_name}: {e}")
58
+
59
+
60
+ # Load plugins
61
+ try:
62
+ from edsl.load_plugins import load_plugins
63
+ from edsl.plugins import get_plugin_manager, get_exports
64
+
65
+ # Load all plugins
66
+ plugins = load_plugins()
67
+ logger.info(f"Loaded {len(plugins)} plugins")
68
+
69
+ # Add plugins to globals and __all__
70
+ for plugin_name, plugin in plugins.items():
71
+ globals()[plugin_name] = plugin
72
+ __all__.append(plugin_name)
73
+ logger.info(f"Registered plugin {plugin_name} in global namespace")
74
+
75
+ # Get exports from plugins and add them to globals
76
+ exports = get_exports()
77
+ logger.info(f"Found {len(exports)} exported objects from plugins")
78
+
79
+ for name, obj in exports.items():
80
+ globals()[name] = obj
81
+ __all__.append(name)
82
+ logger.info(f"Added plugin export: {name}")
83
+
84
+ # Add placeholders for expected exports that are missing
85
+ # This maintains backward compatibility for common plugins
86
+ PLUGIN_PLACEHOLDERS = {
87
+ # No placeholders - removed Conjure for cleaner namespace
88
+ }
89
+
90
+ for placeholder_name, github_url in PLUGIN_PLACEHOLDERS.items():
91
+ if placeholder_name not in globals():
92
+ # Create a placeholder class
93
+ placeholder_class = type(placeholder_name, (), {
94
+ "__getattr__": lambda self, name: self._not_installed(name),
95
+ "_not_installed": lambda self, name: self._raise_import_error(),
96
+ "_raise_import_error": lambda self: exec(f"""
97
+ msg = (
98
+ "The {placeholder_name} plugin is not installed. "
99
+ "To use {placeholder_name} with EDSL, install it using:\\n"
100
+ " from edsl.plugins import install_from_github\\n"
101
+ " install_from_github('{github_url}')\\n"
102
+ "\\nOr from the command line:\\n"
103
+ " edsl plugins install {github_url}"
104
+ )
105
+ logger.warning(msg)
106
+ raise ImportError(msg)
107
+ """)
108
+ })
109
+
110
+ # Register the placeholder
111
+ globals()[placeholder_name] = placeholder_class()
112
+ __all__.append(placeholder_name)
113
+ logger.info(f"Added placeholder for {placeholder_name} with installation instructions")
114
+
115
+ except ImportError as e:
116
+ # Modules not available
117
+ logger.info("Plugin system not available, skipping plugin loading: %s", e)
118
+ logger.debug("Plugin system not available, skipping plugin loading: %s", e)
119
+ except Exception as e:
120
+ # Error loading plugins
121
+ logger.error("Error loading plugins: %s", e)
122
+ logger.debug("Error loading plugins: %s", e)
123
+
124
+ # Now that all modules are loaded, configure logging from the config
125
+ logger.configure_from_config()
126
+
127
+
128
+ # Installs a custom exception handling routine for edsl exceptions
129
+ from .base.base_exception import BaseException
130
+ BaseException.install_exception_hook()
131
+
132
+ # Log the total number of items in __all__ for debugging
133
+ logger.debug(f"EDSL initialization complete with {len(__all__)} items in __all__")
134
+
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.54"
1
+ __version__ = "0.1.55"
edsl/agents/agent.py CHANGED
@@ -481,6 +481,35 @@ class Agent(Base):
481
481
  new_agent.dynamic_traits_function = dynamic_traits_function
482
482
 
483
483
  return new_agent
484
+
485
+ def copy(self) -> Agent:
486
+ """Create a deep copy of this agent using serialization/deserialization.
487
+
488
+ This method uses to_dict/from_dict to create a completely independent copy
489
+ of the agent, including all its traits, codebook, instructions, and special
490
+ functions like dynamic traits and direct answering methods.
491
+
492
+ Returns:
493
+ Agent: A new agent instance that is functionally identical to this one
494
+
495
+ Examples:
496
+ >>> a = Agent(traits={"age": 10, "hair": "brown"},
497
+ ... codebook={'age': 'Their age is'})
498
+ >>> a2 = a.copy()
499
+ >>> a2 == a # Functionally equivalent
500
+ True
501
+ >>> id(a) == id(a2) # But different objects
502
+ False
503
+
504
+ Copy preserves direct answering methods:
505
+
506
+ >>> def f(self, question, scenario): return "I am a direct answer."
507
+ >>> a.add_direct_question_answering_method(f)
508
+ >>> a2 = a.copy()
509
+ >>> a2.answer_question_directly(None, None)
510
+ 'I am a direct answer.'
511
+ """
512
+ return self.duplicate()
484
513
 
485
514
  @property
486
515
  def agent_persona(self) -> Prompt:
edsl/agents/agent_list.py CHANGED
@@ -600,7 +600,42 @@ class AgentList(UserList, Base, AgentListOperationsMixin):
600
600
  return "\n".join(lines)
601
601
  return lines
602
602
 
603
+ @classmethod
604
+ def from_scenario_list(cls, scenario_list: "ScenarioList") -> "AgentList":
605
+ """Create an AgentList from a ScenarioList.
606
+
607
+ This method supports special fields that map to Agent parameters:
608
+ - "name": Will be used as the agent's name
609
+ - "agent_parameters": A dictionary containing:
610
+ - "instruction": The agent's instruction text
611
+ - "name": The agent's name (overrides the "name" field if present)
612
+
613
+ Example:
614
+ >>> from edsl import ScenarioList, Scenario
615
+ >>> # Basic usage with traits
616
+ >>> s = ScenarioList([Scenario({'age': 22, 'hair': 'brown', 'height': 5.5})])
617
+ >>> al = AgentList.from_scenario_list(s)
618
+ >>> al
619
+ AgentList([Agent(traits = {'age': 22, 'hair': 'brown', 'height': 5.5})])
620
+ """
621
+ from .agent import Agent # Use direct relative import
622
+
623
+ agents = []
624
+ for scenario in scenario_list:
625
+ # Simple implementation to handle the basic test case
626
+ new_scenario = scenario.copy().data
627
+ new_agent = Agent(traits=new_scenario)
628
+ agents.append(new_agent)
629
+
630
+ # Add a debug check to verify we've processed the scenarios correctly
631
+ if len(agents) != len(scenario_list):
632
+ raise ValueError(f"Expected {len(scenario_list)} agents, but created {len(agents)}")
633
+
634
+ return cls(agents)
635
+
603
636
 
604
637
  if __name__ == "__main__":
605
638
  import doctest
606
- doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
639
+
640
+ # Just run the standard doctests with verbose flag
641
+ doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)