edsl 0.1.50__py3-none-any.whl → 0.1.52__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 (119) hide show
  1. edsl/__init__.py +45 -34
  2. edsl/__version__.py +1 -1
  3. edsl/base/base_exception.py +2 -2
  4. edsl/buckets/bucket_collection.py +1 -1
  5. edsl/buckets/exceptions.py +32 -0
  6. edsl/buckets/token_bucket_api.py +26 -10
  7. edsl/caching/cache.py +5 -2
  8. edsl/caching/remote_cache_sync.py +5 -5
  9. edsl/caching/sql_dict.py +12 -11
  10. edsl/config/__init__.py +1 -1
  11. edsl/config/config_class.py +4 -2
  12. edsl/conversation/Conversation.py +9 -5
  13. edsl/conversation/car_buying.py +1 -3
  14. edsl/conversation/mug_negotiation.py +2 -6
  15. edsl/coop/__init__.py +11 -8
  16. edsl/coop/coop.py +15 -13
  17. edsl/coop/coop_functions.py +1 -1
  18. edsl/coop/ep_key_handling.py +1 -1
  19. edsl/coop/price_fetcher.py +2 -2
  20. edsl/coop/utils.py +2 -2
  21. edsl/dataset/dataset.py +144 -63
  22. edsl/dataset/dataset_operations_mixin.py +14 -6
  23. edsl/dataset/dataset_tree.py +3 -3
  24. edsl/dataset/display/table_renderers.py +6 -3
  25. edsl/dataset/file_exports.py +4 -4
  26. edsl/dataset/r/ggplot.py +3 -3
  27. edsl/inference_services/available_model_fetcher.py +2 -2
  28. edsl/inference_services/data_structures.py +5 -5
  29. edsl/inference_services/inference_service_abc.py +1 -1
  30. edsl/inference_services/inference_services_collection.py +1 -1
  31. edsl/inference_services/service_availability.py +3 -3
  32. edsl/inference_services/services/azure_ai.py +3 -3
  33. edsl/inference_services/services/google_service.py +1 -1
  34. edsl/inference_services/services/test_service.py +1 -1
  35. edsl/instructions/change_instruction.py +5 -4
  36. edsl/instructions/instruction.py +1 -0
  37. edsl/instructions/instruction_collection.py +5 -4
  38. edsl/instructions/instruction_handler.py +10 -8
  39. edsl/interviews/answering_function.py +20 -21
  40. edsl/interviews/exception_tracking.py +3 -2
  41. edsl/interviews/interview.py +1 -1
  42. edsl/interviews/interview_status_dictionary.py +1 -1
  43. edsl/interviews/interview_task_manager.py +7 -4
  44. edsl/interviews/request_token_estimator.py +3 -2
  45. edsl/interviews/statistics.py +2 -2
  46. edsl/invigilators/invigilators.py +34 -6
  47. edsl/jobs/__init__.py +39 -2
  48. edsl/jobs/async_interview_runner.py +1 -1
  49. edsl/jobs/check_survey_scenario_compatibility.py +5 -5
  50. edsl/jobs/data_structures.py +2 -2
  51. edsl/jobs/html_table_job_logger.py +494 -257
  52. edsl/jobs/jobs.py +2 -2
  53. edsl/jobs/jobs_checks.py +5 -5
  54. edsl/jobs/jobs_component_constructor.py +2 -2
  55. edsl/jobs/jobs_pricing_estimation.py +1 -1
  56. edsl/jobs/jobs_runner_asyncio.py +2 -2
  57. edsl/jobs/jobs_status_enums.py +1 -0
  58. edsl/jobs/remote_inference.py +47 -13
  59. edsl/jobs/results_exceptions_handler.py +2 -2
  60. edsl/language_models/language_model.py +151 -145
  61. edsl/notebooks/__init__.py +24 -1
  62. edsl/notebooks/exceptions.py +82 -0
  63. edsl/notebooks/notebook.py +7 -3
  64. edsl/notebooks/notebook_to_latex.py +1 -1
  65. edsl/prompts/__init__.py +23 -2
  66. edsl/prompts/prompt.py +1 -1
  67. edsl/questions/__init__.py +4 -4
  68. edsl/questions/answer_validator_mixin.py +0 -5
  69. edsl/questions/compose_questions.py +2 -2
  70. edsl/questions/descriptors.py +1 -1
  71. edsl/questions/question_base.py +32 -3
  72. edsl/questions/question_base_prompts_mixin.py +4 -4
  73. edsl/questions/question_budget.py +503 -102
  74. edsl/questions/question_check_box.py +658 -156
  75. edsl/questions/question_dict.py +176 -2
  76. edsl/questions/question_extract.py +401 -61
  77. edsl/questions/question_free_text.py +77 -9
  78. edsl/questions/question_functional.py +118 -9
  79. edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
  80. edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
  81. edsl/questions/question_list.py +246 -26
  82. edsl/questions/question_matrix.py +586 -73
  83. edsl/questions/question_multiple_choice.py +213 -47
  84. edsl/questions/question_numerical.py +360 -29
  85. edsl/questions/question_rank.py +401 -124
  86. edsl/questions/question_registry.py +3 -3
  87. edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
  88. edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
  89. edsl/questions/register_questions_meta.py +2 -1
  90. edsl/questions/response_validator_abc.py +6 -2
  91. edsl/questions/response_validator_factory.py +10 -12
  92. edsl/results/report.py +1 -1
  93. edsl/results/result.py +7 -4
  94. edsl/results/results.py +500 -271
  95. edsl/results/results_selector.py +2 -2
  96. edsl/scenarios/construct_download_link.py +3 -3
  97. edsl/scenarios/scenario.py +1 -2
  98. edsl/scenarios/scenario_list.py +41 -23
  99. edsl/surveys/survey_css.py +3 -3
  100. edsl/surveys/survey_simulator.py +2 -1
  101. edsl/tasks/__init__.py +22 -2
  102. edsl/tasks/exceptions.py +72 -0
  103. edsl/tasks/task_history.py +48 -11
  104. edsl/templates/error_reporting/base.html +37 -4
  105. edsl/templates/error_reporting/exceptions_table.html +105 -33
  106. edsl/templates/error_reporting/interview_details.html +130 -126
  107. edsl/templates/error_reporting/overview.html +21 -25
  108. edsl/templates/error_reporting/report.css +215 -46
  109. edsl/templates/error_reporting/report.js +122 -20
  110. edsl/tokens/__init__.py +27 -1
  111. edsl/tokens/exceptions.py +37 -0
  112. edsl/tokens/interview_token_usage.py +3 -2
  113. edsl/tokens/token_usage.py +4 -3
  114. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/METADATA +1 -1
  115. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/RECORD +118 -116
  116. edsl/questions/derived/__init__.py +0 -0
  117. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/LICENSE +0 -0
  118. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/WHEEL +0 -0
  119. {edsl-0.1.50.dist-info → edsl-0.1.52.dist-info}/entry_points.txt +0 -0
edsl/__init__.py CHANGED
@@ -15,39 +15,42 @@ from edsl import logger
15
15
  # Set up logger with configuration from environment/config
16
16
  # (We'll configure the logger after CONFIG is initialized below)
17
17
 
18
- __all__ = ['logger']
18
+ __all__ = ["logger"]
19
19
 
20
20
  # Define modules to import
21
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'
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
+ "conversation",
34
35
  ]
35
36
 
36
37
  # Dynamically import modules and extend __all__
37
38
  for module_name in modules_to_import:
38
39
  try:
39
40
  # Import the module
40
- module = importlib.import_module(f'.{module_name}', package='edsl')
41
-
41
+ module = importlib.import_module(f".{module_name}", package="edsl")
42
+
42
43
  # Get the module's __all__ attribute
43
- module_all = getattr(module, '__all__', [])
44
-
44
+ module_all = getattr(module, "__all__", [])
45
+
45
46
  # Import all names from the module
46
47
  exec(f"from .{module_name} import *")
47
-
48
+
48
49
  # Extend __all__ with the module's __all__
49
50
  if module_all:
50
- logger.debug(f"Adding {len(module_all)} items from {module_name} to __all__")
51
+ logger.debug(
52
+ f"Adding {len(module_all)} items from {module_name} to __all__"
53
+ )
51
54
  __all__.extend(module_all)
52
55
  else:
53
56
  logger.warning(f"Module {module_name} does not have __all__ defined")
@@ -61,39 +64,43 @@ for module_name in modules_to_import:
61
64
  try:
62
65
  from edsl.load_plugins import load_plugins
63
66
  from edsl.plugins import get_plugin_manager, get_exports
64
-
67
+
65
68
  # Load all plugins
66
69
  plugins = load_plugins()
67
70
  logger.info(f"Loaded {len(plugins)} plugins")
68
-
71
+
69
72
  # Add plugins to globals and __all__
70
73
  for plugin_name, plugin in plugins.items():
71
74
  globals()[plugin_name] = plugin
72
75
  __all__.append(plugin_name)
73
76
  logger.info(f"Registered plugin {plugin_name} in global namespace")
74
-
77
+
75
78
  # Get exports from plugins and add them to globals
76
79
  exports = get_exports()
77
80
  logger.info(f"Found {len(exports)} exported objects from plugins")
78
-
81
+
79
82
  for name, obj in exports.items():
80
83
  globals()[name] = obj
81
84
  __all__.append(name)
82
85
  logger.info(f"Added plugin export: {name}")
83
-
86
+
84
87
  # Add placeholders for expected exports that are missing
85
88
  # This maintains backward compatibility for common plugins
86
89
  PLUGIN_PLACEHOLDERS = {
87
90
  # No placeholders - removed Conjure for cleaner namespace
88
91
  }
89
-
92
+
90
93
  for placeholder_name, github_url in PLUGIN_PLACEHOLDERS.items():
91
94
  if placeholder_name not in globals():
92
95
  # 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"""
96
+ placeholder_class = type(
97
+ placeholder_name,
98
+ (),
99
+ {
100
+ "__getattr__": lambda self, name: self._not_installed(name),
101
+ "_not_installed": lambda self, name: self._raise_import_error(),
102
+ "_raise_import_error": lambda self: exec(
103
+ f"""
97
104
  msg = (
98
105
  "The {placeholder_name} plugin is not installed. "
99
106
  "To use {placeholder_name} with EDSL, install it using:\\n"
@@ -104,13 +111,17 @@ msg = (
104
111
  )
105
112
  logger.warning(msg)
106
113
  raise ImportError(msg)
107
- """)
108
- })
109
-
114
+ """
115
+ ),
116
+ },
117
+ )
118
+
110
119
  # Register the placeholder
111
120
  globals()[placeholder_name] = placeholder_class()
112
121
  __all__.append(placeholder_name)
113
- logger.info(f"Added placeholder for {placeholder_name} with installation instructions")
122
+ logger.info(
123
+ f"Added placeholder for {placeholder_name} with installation instructions"
124
+ )
114
125
 
115
126
  except ImportError as e:
116
127
  # Modules not available
@@ -127,8 +138,8 @@ logger.configure_from_config()
127
138
 
128
139
  # Installs a custom exception handling routine for edsl exceptions
129
140
  from .base.base_exception import BaseException
141
+
130
142
  BaseException.install_exception_hook()
131
143
 
132
144
  # Log the total number of items in __all__ for debugging
133
145
  logger.debug(f"EDSL initialization complete with {len(__all__)} items in __all__")
134
-
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.50"
1
+ __version__ = "0.1.52"
@@ -146,7 +146,7 @@ class BaseException(Exception):
146
146
  # )
147
147
  # )
148
148
  # except:
149
- print(f"❌ EDSL ERROR: {etype.__name__}: {evalue}", file=sys.stderr)
149
+ print(f"❌ E[🦃] EDSL ERROR: {etype.__name__}: {evalue}", file=sys.stderr)
150
150
  # Suppress IPython’s normal traceback
151
151
  return
152
152
  # Otherwise, fall back to the usual traceback
@@ -178,7 +178,7 @@ class BaseException(Exception):
178
178
  # )
179
179
  # except:
180
180
  print(
181
- f"❌ EDSL ERROR: {exc_type.__name__}: {exc_value}",
181
+ f"❌ E[🦃]EDSL ERROR: {exc_type.__name__}: {exc_value}",
182
182
  exc_traceback,
183
183
  file=sys.stderr,
184
184
  )
@@ -135,7 +135,7 @@ class BucketCollection(UserDict):
135
135
  >>> # The following would raise an exception:
136
136
  >>> # bucket_collection.get_tokens(m, 'tokens', 10)
137
137
  """
138
- from edsl.buckets.exceptions import BucketError
138
+ from .exceptions import BucketError
139
139
  raise BucketError("This method is deprecated and should not be used")
140
140
  # The following code is kept for reference only
141
141
  # relevant_bucket = getattr(self[model], bucket_type)
@@ -72,4 +72,36 @@ class BucketConfigurationError(BucketError):
72
72
  TokenBucket(name="test", capacity=-100, refill_rate=10) # Would raise BucketConfigurationError
73
73
  ```
74
74
  """
75
+ relevant_doc = "https://docs.expectedparrot.com/"
76
+
77
+
78
+ class BucketNotFoundError(BucketError):
79
+ """
80
+ Exception raised when a requested bucket cannot be found.
81
+
82
+ This exception occurs when attempting to access a bucket that doesn't exist
83
+ in the system or has been removed.
84
+
85
+ Examples:
86
+ ```python
87
+ # Attempting to access a non-existent bucket:
88
+ bucket_collection.get_bucket("non_existent_bucket") # Would raise BucketNotFoundError
89
+ ```
90
+ """
91
+ relevant_doc = "https://docs.expectedparrot.com/"
92
+
93
+
94
+ class InvalidBucketParameterError(BucketConfigurationError):
95
+ """
96
+ Exception raised when an invalid parameter is provided for bucket operations.
97
+
98
+ This exception occurs when providing invalid parameters to bucket methods,
99
+ such as negative token amounts, invalid capacity values, etc.
100
+
101
+ Examples:
102
+ ```python
103
+ # Attempting to use invalid parameters:
104
+ bucket.add_tokens(-100) # Would raise InvalidBucketParameterError
105
+ ```
106
+ """
75
107
  relevant_doc = "https://docs.expectedparrot.com/"
@@ -1,9 +1,11 @@
1
- from fastapi import FastAPI, HTTPException
1
+ from fastapi import FastAPI
2
+ from fastapi.responses import JSONResponse
2
3
  from pydantic import BaseModel
3
4
  from typing import Union, Dict
4
5
  from typing import Optional
5
6
 
6
7
  from .token_bucket import TokenBucket # Original implementation
8
+ from .exceptions import BucketNotFoundError, InvalidBucketParameterError
7
9
 
8
10
  def safe_float_for_json(value: float) -> Union[float, str]:
9
11
  """Convert float('inf') to 'infinity' for JSON serialization.
@@ -24,6 +26,20 @@ app = FastAPI()
24
26
  # In-memory storage for TokenBucket instances
25
27
  buckets: Dict[str, TokenBucket] = {}
26
28
 
29
+ @app.exception_handler(BucketNotFoundError)
30
+ async def bucket_not_found_handler(request, exc):
31
+ return JSONResponse(
32
+ status_code=404,
33
+ content={"detail": str(exc)},
34
+ )
35
+
36
+ @app.exception_handler(InvalidBucketParameterError)
37
+ async def invalid_parameter_handler(request, exc):
38
+ return JSONResponse(
39
+ status_code=400,
40
+ content={"detail": str(exc)},
41
+ )
42
+
27
43
 
28
44
  class TokenBucketCreate(BaseModel):
29
45
  bucket_name: str
@@ -83,13 +99,13 @@ async def list_buckets(
83
99
  async def add_tokens(bucket_id: str, amount: float):
84
100
  """Add tokens to an existing bucket."""
85
101
  if bucket_id not in buckets:
86
- raise HTTPException(status_code=404, detail="Bucket not found")
102
+ raise BucketNotFoundError(f"Bucket with ID '{bucket_id}' not found")
87
103
 
88
104
  if not isinstance(amount, (int, float)) or amount != amount: # Check for NaN
89
- raise HTTPException(status_code=400, detail="Invalid amount specified")
105
+ raise InvalidBucketParameterError("Invalid amount specified")
90
106
 
91
107
  if amount == float("inf") or amount == float("-inf"):
92
- raise HTTPException(status_code=400, detail="Amount cannot be infinite")
108
+ raise InvalidBucketParameterError("Amount cannot be infinite")
93
109
 
94
110
  bucket = buckets[bucket_id]
95
111
  bucket.add_tokens(amount)
@@ -124,14 +140,14 @@ async def create_bucket(bucket: TokenBucketCreate):
124
140
  not isinstance(bucket.capacity, (int, float))
125
141
  or bucket.capacity != bucket.capacity
126
142
  ): # Check for NaN
127
- raise HTTPException(status_code=400, detail="Invalid capacity value")
143
+ raise InvalidBucketParameterError("Invalid capacity value")
128
144
  if (
129
145
  not isinstance(bucket.refill_rate, (int, float))
130
146
  or bucket.refill_rate != bucket.refill_rate
131
147
  ): # Check for NaN
132
- raise HTTPException(status_code=400, detail="Invalid refill rate value")
148
+ raise InvalidBucketParameterError("Invalid refill rate value")
133
149
  if bucket.capacity == float("inf") or bucket.refill_rate == float("inf"):
134
- raise HTTPException(status_code=400, detail="Values cannot be infinite")
150
+ raise InvalidBucketParameterError("Values cannot be infinite")
135
151
  bucket_id = f"{bucket.bucket_name}_{bucket.bucket_type}"
136
152
  if bucket_id in buckets:
137
153
  # Instead of error, return success with "existing" status
@@ -156,7 +172,7 @@ async def create_bucket(bucket: TokenBucketCreate):
156
172
  @app.post("/bucket/{bucket_id}/get_tokens")
157
173
  async def get_tokens(bucket_id: str, amount: float, cheat_bucket_capacity: bool = True):
158
174
  if bucket_id not in buckets:
159
- raise HTTPException(status_code=404, detail="Bucket not found")
175
+ raise BucketNotFoundError(f"Bucket with ID '{bucket_id}' not found")
160
176
 
161
177
  bucket = buckets[bucket_id]
162
178
  await bucket.get_tokens(amount, cheat_bucket_capacity)
@@ -166,7 +182,7 @@ async def get_tokens(bucket_id: str, amount: float, cheat_bucket_capacity: bool
166
182
  @app.post("/bucket/{bucket_id}/turbo_mode/{state}")
167
183
  async def set_turbo_mode(bucket_id: str, state: bool):
168
184
  if bucket_id not in buckets:
169
- raise HTTPException(status_code=404, detail="Bucket not found")
185
+ raise BucketNotFoundError(f"Bucket with ID '{bucket_id}' not found")
170
186
 
171
187
  bucket = buckets[bucket_id]
172
188
  if state:
@@ -179,7 +195,7 @@ async def set_turbo_mode(bucket_id: str, state: bool):
179
195
  @app.get("/bucket/{bucket_id}/status")
180
196
  async def get_bucket_status(bucket_id: str):
181
197
  if bucket_id not in buckets:
182
- raise HTTPException(status_code=404, detail="Bucket not found")
198
+ raise BucketNotFoundError(f"Bucket with ID '{bucket_id}' not found")
183
199
 
184
200
  bucket = buckets[bucket_id]
185
201
  status = {
edsl/caching/cache.py CHANGED
@@ -176,8 +176,11 @@ class Cache(Base):
176
176
 
177
177
  Examples:
178
178
  >>> from edsl import Cache
179
- >>> Cache.example().values()
180
- [CacheEntry(...)]
179
+ >>> entries = Cache.example().values()
180
+ >>> len(entries)
181
+ 1
182
+ >>> entries[0] # doctest: +ELLIPSIS
183
+ CacheEntry(model='gpt-3.5-turbo', parameters={'temperature': 0.5}, ...)
181
184
  """
182
185
  return list(self.data.values())
183
186
 
@@ -5,7 +5,7 @@ from collections import UserList
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from .cache import Cache
8
- from edsl.coop.coop import Coop
8
+ from ..coop.coop import Coop
9
9
  from .cache_entry import CacheEntry
10
10
 
11
11
 
@@ -34,7 +34,7 @@ class CacheEntriesList(UserList):
34
34
  return f"CacheEntries({entries_repr})"
35
35
 
36
36
  def to_cache(self) -> "Cache":
37
- from edsl.caching.cache import Cache
37
+ from .cache import Cache
38
38
 
39
39
  return Cache({entry.key: entry for entry in self.data})
40
40
 
@@ -178,9 +178,9 @@ if __name__ == "__main__":
178
178
 
179
179
  doctest.testmod()
180
180
 
181
- from edsl.coop.coop import Coop
182
- from edsl.data.Cache import Cache
183
- from edsl.data.CacheEntry import CacheEntry
181
+ from ..coop.coop import Coop
182
+ from .cache import Cache
183
+ from .cache_entry import CacheEntry
184
184
 
185
185
  r = RemoteCacheSync(Coop(), Cache(), print)
186
186
  diff = r._get_cache_difference()
edsl/caching/sql_dict.py CHANGED
@@ -64,8 +64,9 @@ class SQLiteDict:
64
64
 
65
65
  Example:
66
66
  >>> temp_db_path = SQLiteDict._get_temp_path()
67
- >>> SQLiteDict(f"sqlite:///{temp_db_path}") # Use the temp file for SQLite
68
- SQLiteDict(db_path='...')
67
+ >>> db = SQLiteDict(f"sqlite:///{temp_db_path}") # Use the temp file for SQLite
68
+ >>> isinstance(db, SQLiteDict)
69
+ True
69
70
  >>> import os; os.unlink(temp_db_path) # Clean up the temp file after the test
70
71
  """
71
72
  from sqlalchemy.exc import SQLAlchemyError
@@ -76,13 +77,13 @@ class SQLiteDict:
76
77
  if not self.db_path.startswith("sqlite:///"):
77
78
  self.db_path = f"sqlite:///{self.db_path}"
78
79
  try:
79
- from edsl.caching.orm import Base
80
+ from .orm import Base
80
81
 
81
82
  self.engine = create_engine(self.db_path, echo=False, future=True)
82
83
  Base.metadata.create_all(self.engine)
83
84
  self.Session = sessionmaker(bind=self.engine)
84
85
  except SQLAlchemyError as e:
85
- from edsl.caching.exceptions import CacheError
86
+ from .exceptions import CacheError
86
87
  raise CacheError(
87
88
  f"""Database initialization error: {e}. The attempted DB path was {db_path}"""
88
89
  ) from e
@@ -123,10 +124,10 @@ class SQLiteDict:
123
124
  >>> d["foo"] = CacheEntry.example()
124
125
  """
125
126
  if not isinstance(value, CacheEntry):
126
- from edsl.caching.exceptions import CacheValueError
127
+ from .exceptions import CacheValueError
127
128
  raise CacheValueError(f"Value must be a CacheEntry object (got {type(value)}).")
128
129
  with self.Session() as db:
129
- from edsl.caching.orm import Data
130
+ from .orm import Data
130
131
 
131
132
  db.merge(Data(key=key, value=json.dumps(value.to_dict())))
132
133
  db.commit()
@@ -155,11 +156,11 @@ class SQLiteDict:
155
156
  True
156
157
  """
157
158
  with self.Session() as db:
158
- from edsl.caching.orm import Data
159
+ from .orm import Data
159
160
 
160
161
  value = db.query(Data).filter_by(key=key).first()
161
162
  if not value:
162
- from edsl.caching.exceptions import CacheKeyError
163
+ from .exceptions import CacheKeyError
163
164
  raise CacheKeyError(f"Key '{key}' not found.")
164
165
  return CacheEntry.from_dict(json.loads(value.value))
165
166
 
@@ -183,7 +184,7 @@ class SQLiteDict:
183
184
  >>> d.get("foo", "bar")
184
185
  'bar'
185
186
  """
186
- from edsl.caching.exceptions import CacheKeyError
187
+ from .exceptions import CacheKeyError
187
188
  try:
188
189
  return self[key]
189
190
  except (KeyError, CacheKeyError):
@@ -236,7 +237,7 @@ class SQLiteDict:
236
237
  the database from being locked for too long.
237
238
  """
238
239
  if not (isinstance(new_d, dict) or isinstance(new_d, SQLiteDict)):
239
- from edsl.caching.exceptions import CacheValueError
240
+ from .exceptions import CacheValueError
240
241
  raise CacheValueError(
241
242
  f"new_d must be a dict or SQLiteDict object (got {type(new_d)})"
242
243
  )
@@ -305,7 +306,7 @@ class SQLiteDict:
305
306
  db.delete(instance)
306
307
  db.commit()
307
308
  else:
308
- from edsl.caching.exceptions import CacheKeyError
309
+ from .exceptions import CacheKeyError
309
310
  raise CacheKeyError(f"Key '{key}' not found.")
310
311
 
311
312
  def __contains__(self, key: str) -> bool:
edsl/config/__init__.py CHANGED
@@ -3,6 +3,6 @@
3
3
  This module provides a Config class that loads environment variables from a .env file and sets them as class attributes.
4
4
  """
5
5
 
6
- from edsl.config.config_class import Config, CONFIG, CONFIG_MAP, EDSL_RUN_MODES, cache_dir
6
+ from .config_class import Config, CONFIG, CONFIG_MAP, EDSL_RUN_MODES, cache_dir
7
7
 
8
8
  __all__ = ["Config", "CONFIG", "CONFIG_MAP", "EDSL_RUN_MODES", "cache_dir"]
@@ -3,8 +3,10 @@
3
3
  import os
4
4
  import platformdirs
5
5
  from dotenv import load_dotenv, find_dotenv
6
- from edsl.base import BaseException
7
- from edsl import logger
6
+ from ..base import BaseException
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
8
10
 
9
11
  class InvalidEnvironmentVariableError(BaseException):
10
12
  """Raised when an environment variable is invalid."""
@@ -1,13 +1,16 @@
1
1
  from collections import UserList
2
2
  import asyncio
3
3
  import inspect
4
- from typing import Optional, Callable
5
- from .. import QuestionFreeText, Results, AgentList, ScenarioList, Scenario
4
+ from typing import Optional, Callable, TYPE_CHECKING
5
+ from .. import QuestionFreeText, Results, AgentList, ScenarioList, Scenario, Model
6
6
  from ..questions import QuestionBase
7
- from ..results.Result import Result
7
+ from ..results.result import Result
8
8
  from jinja2 import Template
9
9
  from ..caching import Cache
10
10
 
11
+ if TYPE_CHECKING:
12
+ from ..language_models.model import Model
13
+
11
14
  from .next_speaker_utilities import (
12
15
  default_turn_taking_generator,
13
16
  speaker_closure,
@@ -71,7 +74,7 @@ class Conversation:
71
74
  conversation_index: Optional[int] = None,
72
75
  cache=None,
73
76
  disable_remote_inference=False,
74
- default_model: Optional["LanguageModel"] = None,
77
+ default_model: Optional[Model] = None,
75
78
  ):
76
79
  self.disable_remote_inference = disable_remote_inference
77
80
  self.per_round_message_template = per_round_message_template
@@ -120,7 +123,8 @@ What do you say next?"""
120
123
  per_round_message_template
121
124
  and "{{ round_message }}" not in next_statement_question.question_text
122
125
  ):
123
- from edsl.conversation.exceptions import ConversationValueError
126
+ from .exceptions import ConversationValueError
127
+
124
128
  raise ConversationValueError(
125
129
  "If you pass in a per_round_message_template, you must include {{ round_message }} in the question_text."
126
130
  )
@@ -1,5 +1,4 @@
1
- from .. import Agent, AgentList, QuestionFreeText
2
- from .. import Cache
1
+ from .. import Agent, AgentList, QuestionFreeText, Cache, QuestionList
3
2
  from .Conversation import Conversation, ConversationList
4
3
 
5
4
  a1 = Agent(
@@ -46,7 +45,6 @@ q = QuestionFreeText(
46
45
  question_name="car_brand",
47
46
  )
48
47
 
49
- from .. import QuestionList
50
48
 
51
49
  q_actors = QuestionList(
52
50
  question_text="""This was a conversation about buying a car: {{ transcript }}.
@@ -1,5 +1,5 @@
1
- from edsl import Agent, AgentList
2
- from edsl.conversation.Conversation import Conversation, ConversationList
1
+ from .. import Agent, AgentList, QuestionYesNo, QuestionNumerical
2
+ from .Conversation import Conversation, ConversationList
3
3
 
4
4
 
5
5
  def bargaining_pairs(alice_valuation, bob_valuation):
@@ -43,10 +43,6 @@ results.select("conversation_index", "index", "agent_name", "dialogue").print(
43
43
  format="rich"
44
44
  )
45
45
 
46
- from edsl import (
47
- QuestionYesNo,
48
- QuestionNumerical,
49
- )
50
46
 
51
47
  q_deal = QuestionYesNo(
52
48
  question_text="""This was a negotiation: {{ transcript }}.
edsl/coop/__init__.py CHANGED
@@ -13,16 +13,19 @@ This module enables EDSL to interact with cloud-based resources for enhanced fun
13
13
  The primary interface is the Coop class, which serves as a client for the
14
14
  Expected Parrot API. Most users will only need to interact with the Coop class directly.
15
15
 
16
- Example:
17
- >>> from edsl.coop import Coop
18
- >>> coop = Coop() # Uses API key from environment or stored location
19
- >>> survey = my_survey.push() # Uploads survey to Expected Parrot
20
- >>> job_info = coop.remote_inference_create(my_job) # Creates remote job
16
+ Examples:
17
+
18
+ ```python
19
+ from edsl.coop import Coop
20
+ coop = Coop() # Uses API key from environment or stored location
21
+ survey = my_survey.push() # Uploads survey to Expected Parrot
22
+ job_info = coop.remote_inference_create(my_job) # Creates remote job
21
23
 
22
24
  # Working with plugins
23
- >>> from edsl.coop import get_available_plugins
24
- >>> plugins = get_available_plugins()
25
- >>> plugin_names = [p.name for p in plugins]
25
+ from edsl.coop import get_available_plugins
26
+ plugins = get_available_plugins()
27
+ plugin_names = [p.name for p in plugins]
28
+ ```
26
29
  """
27
30
 
28
31
  from .utils import EDSLObject, ObjectType, VisibilityType, ObjectRegistry