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
@@ -21,12 +21,12 @@ from .exceptions import CacheError
21
21
  class CacheEntry(RepresentationMixin):
22
22
  """
23
23
  Represents a single cached language model response with associated metadata.
24
-
24
+
25
25
  CacheEntry objects store language model responses along with the prompts and
26
26
  parameters that generated them. Each entry is uniquely identified by a hash
27
- of its key fields (model, parameters, prompts, and iteration), making it
27
+ of its key fields (model, parameters, prompts, and iteration), making it
28
28
  possible to efficiently retrieve cached responses for identical inputs.
29
-
29
+
30
30
  Attributes:
31
31
  model (str): The language model identifier (e.g., "gpt-3.5-turbo")
32
32
  parameters (dict): Model parameters used for generation (e.g., temperature)
@@ -37,14 +37,14 @@ class CacheEntry(RepresentationMixin):
37
37
  with the same prompts (defaults to 0)
38
38
  timestamp (int): Unix timestamp when the entry was created
39
39
  service (str, optional): The service provider for the model (e.g., "openai")
40
-
40
+
41
41
  Class Attributes:
42
42
  key_fields (List[str]): Fields used to generate the unique hash key
43
43
  all_fields (List[str]): All fields stored in the cache entry
44
44
  """
45
45
 
46
46
  key_fields = ["model", "parameters", "system_prompt", "user_prompt", "iteration"]
47
- all_fields = key_fields + ["timestamp", "output", "service"]
47
+ all_fields = key_fields + ["timestamp", "output", "service", "validated"]
48
48
 
49
49
  def __init__(
50
50
  self,
@@ -57,6 +57,7 @@ class CacheEntry(RepresentationMixin):
57
57
  output: str,
58
58
  timestamp: Optional[int] = None,
59
59
  service: Optional[str] = None,
60
+ validated: bool = False,
60
61
  ):
61
62
  self.model = model
62
63
  self.parameters = parameters
@@ -68,16 +69,17 @@ class CacheEntry(RepresentationMixin):
68
69
  datetime.datetime.now(datetime.timezone.utc).timestamp()
69
70
  )
70
71
  self.service = service
72
+ self.validated = validated
71
73
  self._check_types()
72
74
 
73
75
  def _check_types(self) -> None:
74
76
  """
75
77
  Validates that all attributes have the correct types.
76
-
78
+
77
79
  This method is called during initialization to ensure that all
78
80
  attributes have the expected types, raising TypeError exceptions
79
81
  with descriptive messages when validation fails.
80
-
82
+
81
83
  Raises:
82
84
  TypeError: If any attribute has an incorrect type
83
85
  """
@@ -98,31 +100,38 @@ class CacheEntry(RepresentationMixin):
98
100
  raise CacheError("`timestamp` should be an integer")
99
101
  if self.service is not None and not isinstance(self.service, str):
100
102
  raise CacheError("`service` should be either a string or None")
103
+ if not isinstance(self.validated, bool):
104
+ raise CacheError("`validated` should be a boolean")
101
105
 
102
106
  @classmethod
103
107
  def gen_key(
104
- cls, *, model: str, parameters: Dict[str, Any],
105
- system_prompt: str, user_prompt: str, iteration: int
108
+ cls,
109
+ *,
110
+ model: str,
111
+ parameters: Dict[str, Any],
112
+ system_prompt: str,
113
+ user_prompt: str,
114
+ iteration: int,
106
115
  ) -> str:
107
116
  """
108
117
  Generates a unique key hash for the cache entry based on input parameters.
109
-
118
+
110
119
  This method creates a deterministic hash key by concatenating the model name,
111
120
  parameters (sorted to ensure consistency), system prompt, user prompt, and
112
121
  iteration number. The hash enables efficient lookup of cache entries with
113
122
  identical inputs.
114
-
123
+
115
124
  Args:
116
125
  model: The language model identifier
117
126
  parameters: Dictionary of model parameters (will be sorted for consistency)
118
127
  system_prompt: The system prompt provided to the model
119
128
  user_prompt: The user prompt provided to the model
120
129
  iteration: Iteration number for this combination of inputs
121
-
130
+
122
131
  Returns:
123
132
  A hex-encoded MD5 hash string that uniquely identifies this combination
124
133
  of inputs
125
-
134
+
126
135
  Note:
127
136
  - The hash treats single and double quotes as equivalent
128
137
  - Parameters are sorted to ensure consistent hashing regardless of order
@@ -134,11 +143,11 @@ class CacheEntry(RepresentationMixin):
134
143
  def key(self) -> str:
135
144
  """
136
145
  Returns the unique hash key for this cache entry.
137
-
146
+
138
147
  This property extracts the key fields from the instance and generates
139
148
  a hash key using the gen_key classmethod. The key uniquely identifies
140
149
  this combination of model, parameters, prompts, and iteration.
141
-
150
+
142
151
  Returns:
143
152
  A hex-encoded MD5 hash string that uniquely identifies this cache entry
144
153
  """
@@ -148,17 +157,17 @@ class CacheEntry(RepresentationMixin):
148
157
  def to_dict(self, add_edsl_version: bool = True) -> Dict[str, Any]:
149
158
  """
150
159
  Converts the cache entry to a dictionary representation.
151
-
160
+
152
161
  This method creates a dictionary containing all fields of the cache entry,
153
162
  making it suitable for serialization or storage.
154
-
163
+
155
164
  Args:
156
165
  add_edsl_version: If True, adds EDSL version information to the dict
157
166
  (Currently disabled pending implementation)
158
-
167
+
159
168
  Returns:
160
169
  A dictionary representation of the cache entry with all fields
161
-
170
+
162
171
  Note:
163
172
  The edsl_version feature is currently disabled in the implementation
164
173
  """
@@ -171,6 +180,7 @@ class CacheEntry(RepresentationMixin):
171
180
  "iteration": self.iteration,
172
181
  "timestamp": self.timestamp,
173
182
  "service": self.service,
183
+ "validated": self.validated,
174
184
  }
175
185
  # Feature for adding version information (currently disabled)
176
186
  # if add_edsl_version:
@@ -182,9 +192,9 @@ class CacheEntry(RepresentationMixin):
182
192
  def keys(self) -> List[str]:
183
193
  """
184
194
  Returns a list of field names in this cache entry.
185
-
195
+
186
196
  This method enables dict-like access to cache entry field names.
187
-
197
+
188
198
  Returns:
189
199
  List of field names from the dictionary representation
190
200
  """
@@ -193,9 +203,9 @@ class CacheEntry(RepresentationMixin):
193
203
  def values(self) -> List[Any]:
194
204
  """
195
205
  Returns a list of values for all fields in this cache entry.
196
-
206
+
197
207
  This method enables dict-like access to cache entry values.
198
-
208
+
199
209
  Returns:
200
210
  List of values from the dictionary representation
201
211
  """
@@ -204,16 +214,16 @@ class CacheEntry(RepresentationMixin):
204
214
  def __getitem__(self, key: str) -> Any:
205
215
  """
206
216
  Enables dictionary-style access to cache entry attributes.
207
-
217
+
208
218
  This method allows accessing cache entry attributes using dictionary
209
219
  syntax (e.g., entry["model"] instead of entry.model).
210
-
220
+
211
221
  Args:
212
222
  key: The name of the attribute to access
213
-
223
+
214
224
  Returns:
215
225
  The value of the specified attribute
216
-
226
+
217
227
  Raises:
218
228
  AttributeError: If the specified attribute doesn't exist
219
229
  """
@@ -223,17 +233,17 @@ class CacheEntry(RepresentationMixin):
223
233
  def from_dict(cls, data: Dict[str, Any]) -> CacheEntry:
224
234
  """
225
235
  Creates a CacheEntry object from a dictionary representation.
226
-
236
+
227
237
  This factory method enables reconstruction of CacheEntry objects
228
238
  from serialized dictionary representations, such as those produced
229
239
  by the to_dict method.
230
-
240
+
231
241
  Args:
232
242
  data: Dictionary containing required CacheEntry fields
233
-
243
+
234
244
  Returns:
235
245
  A new CacheEntry instance with fields populated from the dictionary
236
-
246
+
237
247
  Raises:
238
248
  TypeError: If data contains fields with incorrect types
239
249
  KeyError: If required fields are missing from data
@@ -243,18 +253,18 @@ class CacheEntry(RepresentationMixin):
243
253
  def __eq__(self, other: Any) -> bool:
244
254
  """
245
255
  Compares this cache entry with another for equality.
246
-
256
+
247
257
  This method checks if all fields except timestamp are equal between
248
- this cache entry and another. The timestamp is excluded from the
258
+ this cache entry and another. The timestamp is excluded from the
249
259
  comparison because it's typically not relevant for determining if
250
260
  two entries represent the same cached response.
251
-
261
+
252
262
  Args:
253
263
  other: Another object to compare with this cache entry
254
-
264
+
255
265
  Returns:
256
266
  True if all fields except timestamp are equal, False otherwise
257
-
267
+
258
268
  Note:
259
269
  Returns False if other is not a CacheEntry instance
260
270
  """
@@ -268,11 +278,11 @@ class CacheEntry(RepresentationMixin):
268
278
  def __repr__(self) -> str:
269
279
  """
270
280
  Returns a string representation of this cache entry.
271
-
281
+
272
282
  This method creates a string representation that displays all fields
273
283
  of the cache entry in a format that can be evaluated to recreate
274
284
  the object.
275
-
285
+
276
286
  Returns:
277
287
  A string representation that can be passed to eval() to recreate
278
288
  this cache entry
@@ -285,24 +295,25 @@ class CacheEntry(RepresentationMixin):
285
295
  f"output={repr(self.output)}, "
286
296
  f"iteration={self.iteration}, "
287
297
  f"timestamp={self.timestamp}, "
288
- f"service={repr(self.service)})"
298
+ f"service={repr(self.service)}, "
299
+ f"validated={self.validated})"
289
300
  )
290
301
 
291
302
  @classmethod
292
303
  def example(cls, randomize: bool = False) -> CacheEntry:
293
304
  """
294
305
  Creates an example CacheEntry instance for testing and demonstration.
295
-
306
+
296
307
  This factory method generates a pre-populated CacheEntry with example
297
308
  values, useful for testing, documentation, and examples.
298
-
309
+
299
310
  Args:
300
311
  randomize: If True, adds a random UUID to the system prompt to make
301
312
  the entry unique and generate a different hash key
302
-
313
+
303
314
  Returns:
304
315
  A fully populated example CacheEntry instance
305
-
316
+
306
317
  Example:
307
318
  >>> entry = CacheEntry.example()
308
319
  >>> isinstance(entry, CacheEntry)
@@ -320,20 +331,21 @@ class CacheEntry(RepresentationMixin):
320
331
  iteration=1,
321
332
  timestamp=int(datetime.datetime.now(datetime.timezone.utc).timestamp()),
322
333
  service="openai",
334
+ validated=True,
323
335
  )
324
336
 
325
337
  @classmethod
326
338
  def example_dict(cls) -> Dict[str, CacheEntry]:
327
339
  """
328
340
  Creates an example dictionary mapping a key to a CacheEntry.
329
-
341
+
330
342
  This method demonstrates how CacheEntry objects are typically stored
331
343
  in a cache, with their hash keys as dictionary keys.
332
-
344
+
333
345
  Returns:
334
346
  A dictionary with a single entry mapping the example entry's key
335
347
  to the example entry
336
-
348
+
337
349
  Note:
338
350
  This is particularly useful for testing and demonstrating the
339
351
  Cache class functionality
@@ -345,13 +357,13 @@ class CacheEntry(RepresentationMixin):
345
357
  def fetch_input_example(cls) -> Dict[str, Any]:
346
358
  """
347
359
  Creates an example input dictionary for a 'fetch' operation.
348
-
360
+
349
361
  This method generates a dictionary containing the fields needed to
350
362
  look up a cache entry (everything except the response/output fields).
351
-
363
+
352
364
  Returns:
353
365
  A dictionary with fields needed to generate a cache key for lookup
354
-
366
+
355
367
  Note:
356
368
  This is used by the Cache class to demonstrate fetch operations
357
369
  """
@@ -365,14 +377,14 @@ class CacheEntry(RepresentationMixin):
365
377
  def store_input_example(cls) -> Dict[str, Any]:
366
378
  """
367
379
  Creates an example input dictionary for a 'store' operation.
368
-
380
+
369
381
  This method generates a dictionary containing the fields needed to
370
382
  store a new cache entry, with 'output' renamed to 'response' to match
371
383
  the API of the Cache.store method.
372
-
384
+
373
385
  Returns:
374
386
  A dictionary with fields needed to store a new cache entry
375
-
387
+
376
388
  Note:
377
389
  This is used by the Cache class to demonstrate store operations
378
390
  """
@@ -385,11 +397,11 @@ class CacheEntry(RepresentationMixin):
385
397
  def main() -> None:
386
398
  """
387
399
  Demonstration of CacheEntry functionality for interactive testing.
388
-
400
+
389
401
  This function demonstrates the key features of the CacheEntry class,
390
402
  including creating entries, calculating hash keys, converting to/from
391
403
  dictionaries, and comparing entries.
392
-
404
+
393
405
  Note:
394
406
  This function is intended to be run in an interactive Python session
395
407
  for exploration and testing, not as part of normal code execution.
@@ -402,20 +414,20 @@ def main() -> None:
402
414
 
403
415
  # Demonstrate key generation
404
416
  print(f"Cache key: {cache_entry.key}")
405
-
417
+
406
418
  # Demonstrate serialization and deserialization
407
419
  entry_dict = cache_entry.to_dict()
408
420
  print(f"Dictionary representation: {entry_dict}")
409
421
  reconstructed = CacheEntry.from_dict(entry_dict)
410
422
  print(f"Reconstructed from dict: {reconstructed}")
411
-
423
+
412
424
  # Demonstrate equality comparisons
413
425
  print(f"Same content equals: {cache_entry == CacheEntry.example()}")
414
426
  print(f"Same key equals: {cache_entry.key == CacheEntry.example().key}")
415
-
427
+
416
428
  # Demonstrate repr evaluation
417
429
  print(f"Repr can be evaluated: {eval(repr(cache_entry)) == cache_entry}")
418
-
430
+
419
431
  # Demonstrate utility methods
420
432
  print(f"Example dict: {CacheEntry.example_dict()}")
421
433
  print(f"Fetch input example: {CacheEntry.fetch_input_example()}")
edsl/caching/sql_dict.py CHANGED
@@ -363,6 +363,23 @@ class SQLiteDict:
363
363
 
364
364
  def __repr__(self) -> str:
365
365
  return f"{self.__class__.__name__}(db_path={self.db_path!r})"
366
+
367
+ def close(self):
368
+ """Close database connections and clean up resources.
369
+
370
+ This method properly disposes of the SQLAlchemy engine,
371
+ closing all connections in the pool to prevent memory leaks.
372
+ """
373
+ if hasattr(self, 'engine') and self.engine:
374
+ self.engine.dispose()
375
+
376
+ def __del__(self):
377
+ """Destructor for proper resource cleanup.
378
+
379
+ Ensures SQLAlchemy connections are properly closed when the
380
+ object is garbage collected.
381
+ """
382
+ self.close()
366
383
 
367
384
  @classmethod
368
385
  def example(cls) -> SQLiteDict:
edsl/cli.py CHANGED
@@ -6,6 +6,8 @@ This module provides the main entry point for the EDSL command-line tool.
6
6
 
7
7
  import sys
8
8
  import typer
9
+ import json
10
+ from pathlib import Path
9
11
  from typing import Optional
10
12
  from rich.console import Console
11
13
  from importlib import metadata
@@ -20,6 +22,103 @@ from .plugins.cli_typer import app as plugins_app
20
22
  # Add the plugins subcommand
21
23
  app.add_typer(plugins_app, name="plugins")
22
24
 
25
+ # Create the validation app
26
+ validation_app = typer.Typer(help="Manage EDSL validation failures")
27
+ app.add_typer(validation_app, name="validation")
28
+
29
+ @validation_app.command("logs")
30
+ def list_validation_logs(
31
+ count: int = typer.Option(10, "--count", "-n", help="Number of logs to show"),
32
+ question_type: Optional[str] = typer.Option(None, "--type", "-t", help="Filter by question type"),
33
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output file path"),
34
+ ):
35
+ """List validation failure logs."""
36
+ from .questions.validation_logger import get_validation_failure_logs
37
+
38
+ logs = get_validation_failure_logs(n=count)
39
+
40
+ # Filter by question type if provided
41
+ if question_type:
42
+ logs = [log for log in logs if log.get("question_type") == question_type]
43
+
44
+ if output:
45
+ with open(output, "w") as f:
46
+ json.dump(logs, f, indent=2)
47
+ console.print(f"[green]Logs written to {output}[/green]")
48
+ else:
49
+ console.print_json(json.dumps(logs, indent=2))
50
+
51
+ @validation_app.command("clear")
52
+ def clear_validation_logs():
53
+ """Clear validation failure logs."""
54
+ from .questions.validation_logger import clear_validation_logs
55
+
56
+ clear_validation_logs()
57
+ console.print("[green]Validation logs cleared.[/green]")
58
+
59
+ @validation_app.command("stats")
60
+ def validation_stats(
61
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output file path"),
62
+ ):
63
+ """Show validation failure statistics."""
64
+ from .questions.validation_analysis import get_validation_failure_stats
65
+
66
+ stats = get_validation_failure_stats()
67
+
68
+ if output:
69
+ with open(output, "w") as f:
70
+ json.dump(stats, f, indent=2)
71
+ console.print(f"[green]Stats written to {output}[/green]")
72
+ else:
73
+ console.print_json(json.dumps(stats, indent=2))
74
+
75
+ @validation_app.command("suggest")
76
+ def suggest_improvements(
77
+ question_type: Optional[str] = typer.Option(None, "--type", "-t", help="Filter by question type"),
78
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output file path"),
79
+ ):
80
+ """Suggest improvements for fix methods."""
81
+ from .questions.validation_analysis import suggest_fix_improvements
82
+
83
+ suggestions = suggest_fix_improvements(question_type=question_type)
84
+
85
+ if output:
86
+ with open(output, "w") as f:
87
+ json.dump(suggestions, f, indent=2)
88
+ console.print(f"[green]Suggestions written to {output}[/green]")
89
+ else:
90
+ console.print_json(json.dumps(suggestions, indent=2))
91
+
92
+ @validation_app.command("report")
93
+ def generate_report(
94
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output file path"),
95
+ ):
96
+ """Generate a comprehensive validation report."""
97
+ from .questions.validation_analysis import export_improvements_report
98
+
99
+ report_path = export_improvements_report(output_path=output)
100
+ console.print(f"[green]Report generated at: {report_path}[/green]")
101
+
102
+ @validation_app.command("html-report")
103
+ def generate_html_report(
104
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output file path"),
105
+ open_browser: bool = typer.Option(True, "--open/--no-open", help="Open the report in a browser"),
106
+ ):
107
+ """Generate an HTML validation report and optionally open it in a browser."""
108
+ from .questions.validation_html_report import generate_html_report
109
+ import webbrowser
110
+
111
+ report_path = generate_html_report(output_path=output)
112
+ console.print(f"[green]HTML report generated at: {report_path}[/green]")
113
+
114
+ if open_browser:
115
+ try:
116
+ webbrowser.open(f"file://{report_path}")
117
+ console.print("[green]Opened report in browser[/green]")
118
+ except Exception as e:
119
+ console.print(f"[yellow]Could not open browser: {e}[/yellow]")
120
+ console.print(f"[yellow]Report is available at: {report_path}[/yellow]")
121
+
23
122
  @app.callback()
24
123
  def callback():
25
124
  """
@@ -58,6 +58,10 @@ CONFIG_MAP = {
58
58
  "default": "True",
59
59
  "info": "This config var determines whether to fetch prices for tokens used in remote inference",
60
60
  },
61
+ "EDSL_LOG_DIR": {
62
+ "default": str(os.path.join(platformdirs.user_data_dir('edsl'), 'logs')),
63
+ "info": "This config var determines the directory where logs are stored.",
64
+ },
61
65
  "EDSL_LOG_LEVEL": {
62
66
  "default": "ERROR",
63
67
  "info": "This config var determines the logging level for the EDSL package (DEBUG, INFO, WARNING, ERROR, CRITICAL).",
@@ -90,6 +94,18 @@ CONFIG_MAP = {
90
94
  "default": "None",
91
95
  "info": "This config var holds the URL of the remote token bucket server.",
92
96
  },
97
+ "EDSL_SQLLIST_MEMORY_THRESHOLD": {
98
+ "default": "10", # Change to a very low threshold (10 bytes) to test SQLite offloading
99
+ "info": "This config var determines the memory threshold in bytes before SQLList offloads data to SQLite.",
100
+ },
101
+ "EDSL_SQLLIST_DB_PATH": {
102
+ "default": f"sqlite:///{os.path.join(platformdirs.user_cache_dir('edsl'), 'sql_list_data.db')}",
103
+ "info": "This config var determines the default database path for SQLList instances.",
104
+ },
105
+ "EDSL_RESULTS_MEMORY_THRESHOLD": {
106
+ "default": "10", # Change to a very low threshold (10 bytes) to test SQLite offloading
107
+ "info": "This config var determines the memory threshold in bytes before Results' SQLList offloads data to SQLite.",
108
+ },
93
109
  }
94
110
 
95
111
 
@@ -0,0 +1,31 @@
1
+ """
2
+ The conversation module provides tools for simulating conversations between agents.
3
+
4
+ It includes classes for managing dialogues, tracking statements, and controlling
5
+ conversation flow between multiple participants.
6
+ """
7
+
8
+ from .Conversation import Conversation, ConversationList, AgentStatement, AgentStatements
9
+ from .exceptions import ConversationError, ConversationValueError, ConversationStateError
10
+ from .next_speaker_utilities import (
11
+ default_turn_taking_generator,
12
+ turn_taking_generator_with_focal_speaker,
13
+ random_turn_taking_generator,
14
+ random_inclusive_generator,
15
+ speaker_closure,
16
+ )
17
+
18
+ __all__ = [
19
+ "Conversation",
20
+ "ConversationList",
21
+ "AgentStatement",
22
+ "AgentStatements",
23
+ "ConversationError",
24
+ "ConversationValueError",
25
+ "ConversationStateError",
26
+ "default_turn_taking_generator",
27
+ "turn_taking_generator_with_focal_speaker",
28
+ "random_turn_taking_generator",
29
+ "random_inclusive_generator",
30
+ "speaker_closure",
31
+ ]