edsl 0.1.54__py3-none-any.whl → 0.1.56__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/__init__.py +8 -1
- edsl/__init__original.py +134 -0
- edsl/__version__.py +1 -1
- edsl/agents/agent.py +29 -0
- edsl/agents/agent_list.py +36 -1
- edsl/base/base_class.py +281 -151
- edsl/base/data_transfer_models.py +15 -4
- edsl/buckets/__init__.py +8 -3
- edsl/buckets/bucket_collection.py +9 -3
- edsl/buckets/model_buckets.py +4 -2
- edsl/buckets/token_bucket.py +2 -2
- edsl/buckets/token_bucket_client.py +5 -3
- edsl/caching/cache.py +131 -62
- edsl/caching/cache_entry.py +70 -58
- edsl/caching/sql_dict.py +17 -0
- edsl/cli.py +99 -0
- edsl/config/config_class.py +16 -0
- edsl/conversation/__init__.py +31 -0
- edsl/coop/coop.py +276 -242
- edsl/coop/coop_jobs_objects.py +59 -0
- edsl/coop/coop_objects.py +29 -0
- edsl/coop/coop_regular_objects.py +26 -0
- edsl/coop/utils.py +24 -19
- edsl/dataset/dataset.py +338 -101
- edsl/dataset/dataset_operations_mixin.py +216 -180
- edsl/db_list/sqlite_list.py +349 -0
- edsl/inference_services/__init__.py +40 -5
- edsl/inference_services/exceptions.py +11 -0
- edsl/inference_services/services/anthropic_service.py +5 -2
- edsl/inference_services/services/aws_bedrock.py +6 -2
- edsl/inference_services/services/azure_ai.py +6 -2
- edsl/inference_services/services/google_service.py +7 -3
- edsl/inference_services/services/mistral_ai_service.py +6 -2
- edsl/inference_services/services/open_ai_service.py +6 -2
- edsl/inference_services/services/perplexity_service.py +6 -2
- edsl/inference_services/services/test_service.py +94 -5
- edsl/interviews/answering_function.py +167 -59
- edsl/interviews/interview.py +124 -72
- edsl/interviews/interview_task_manager.py +10 -0
- edsl/interviews/request_token_estimator.py +8 -0
- edsl/invigilators/invigilators.py +35 -13
- edsl/jobs/async_interview_runner.py +146 -104
- edsl/jobs/data_structures.py +6 -4
- edsl/jobs/decorators.py +61 -0
- edsl/jobs/fetch_invigilator.py +61 -18
- edsl/jobs/html_table_job_logger.py +14 -2
- edsl/jobs/jobs.py +180 -104
- edsl/jobs/jobs_component_constructor.py +2 -2
- edsl/jobs/jobs_interview_constructor.py +2 -0
- edsl/jobs/jobs_pricing_estimation.py +154 -113
- edsl/jobs/jobs_remote_inference_logger.py +4 -0
- edsl/jobs/jobs_runner_status.py +30 -25
- edsl/jobs/progress_bar_manager.py +79 -0
- edsl/jobs/remote_inference.py +35 -1
- edsl/key_management/key_lookup_builder.py +6 -1
- edsl/language_models/language_model.py +110 -12
- edsl/language_models/model.py +10 -3
- edsl/language_models/price_manager.py +176 -71
- edsl/language_models/registry.py +5 -0
- edsl/notebooks/notebook.py +77 -10
- edsl/questions/VALIDATION_README.md +134 -0
- edsl/questions/__init__.py +24 -1
- edsl/questions/exceptions.py +21 -0
- edsl/questions/question_dict.py +201 -16
- edsl/questions/question_multiple_choice_with_other.py +624 -0
- edsl/questions/question_registry.py +2 -1
- edsl/questions/templates/multiple_choice_with_other/__init__.py +0 -0
- edsl/questions/templates/multiple_choice_with_other/answering_instructions.jinja +15 -0
- edsl/questions/templates/multiple_choice_with_other/question_presentation.jinja +17 -0
- edsl/questions/validation_analysis.py +185 -0
- edsl/questions/validation_cli.py +131 -0
- edsl/questions/validation_html_report.py +404 -0
- edsl/questions/validation_logger.py +136 -0
- edsl/results/result.py +115 -46
- edsl/results/results.py +702 -171
- edsl/scenarios/construct_download_link.py +16 -3
- edsl/scenarios/directory_scanner.py +226 -226
- edsl/scenarios/file_methods.py +5 -0
- edsl/scenarios/file_store.py +150 -9
- edsl/scenarios/handlers/__init__.py +5 -1
- edsl/scenarios/handlers/mp4_file_store.py +104 -0
- edsl/scenarios/handlers/webm_file_store.py +104 -0
- edsl/scenarios/scenario.py +120 -101
- edsl/scenarios/scenario_list.py +800 -727
- edsl/scenarios/scenario_list_gc_test.py +146 -0
- edsl/scenarios/scenario_list_memory_test.py +214 -0
- edsl/scenarios/scenario_list_source_refactor.md +35 -0
- edsl/scenarios/scenario_selector.py +5 -4
- edsl/scenarios/scenario_source.py +1990 -0
- edsl/scenarios/tests/test_scenario_list_sources.py +52 -0
- edsl/surveys/survey.py +22 -0
- edsl/tasks/__init__.py +4 -2
- edsl/tasks/task_history.py +198 -36
- edsl/tests/scenarios/test_ScenarioSource.py +51 -0
- edsl/tests/scenarios/test_scenario_list_sources.py +51 -0
- edsl/utilities/__init__.py +2 -1
- edsl/utilities/decorators.py +121 -0
- edsl/utilities/memory_debugger.py +1010 -0
- {edsl-0.1.54.dist-info → edsl-0.1.56.dist-info}/METADATA +51 -76
- {edsl-0.1.54.dist-info → edsl-0.1.56.dist-info}/RECORD +103 -79
- edsl/jobs/jobs_runner_asyncio.py +0 -281
- edsl/language_models/unused/fake_openai_service.py +0 -60
- {edsl-0.1.54.dist-info → edsl-0.1.56.dist-info}/LICENSE +0 -0
- {edsl-0.1.54.dist-info → edsl-0.1.56.dist-info}/WHEEL +0 -0
- {edsl-0.1.54.dist-info → edsl-0.1.56.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
|
|
edsl/__init__original.py
ADDED
@@ -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.
|
1
|
+
__version__ = "0.1.56"
|
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
|
-
|
639
|
+
|
640
|
+
# Just run the standard doctests with verbose flag
|
641
|
+
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
|