edsl 0.1.47__py3-none-any.whl → 0.1.49__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 (314) hide show
  1. edsl/__init__.py +44 -39
  2. edsl/__version__.py +1 -1
  3. edsl/agents/__init__.py +4 -2
  4. edsl/agents/{Agent.py → agent.py} +442 -152
  5. edsl/agents/{AgentList.py → agent_list.py} +220 -162
  6. edsl/agents/descriptors.py +46 -7
  7. edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
  8. edsl/base/__init__.py +75 -0
  9. edsl/base/base_class.py +1303 -0
  10. edsl/base/data_transfer_models.py +114 -0
  11. edsl/base/enums.py +215 -0
  12. edsl/base.py +8 -0
  13. edsl/buckets/__init__.py +25 -0
  14. edsl/buckets/bucket_collection.py +324 -0
  15. edsl/buckets/model_buckets.py +206 -0
  16. edsl/buckets/token_bucket.py +502 -0
  17. edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
  18. edsl/buckets/token_bucket_client.py +509 -0
  19. edsl/caching/__init__.py +20 -0
  20. edsl/caching/cache.py +814 -0
  21. edsl/caching/cache_entry.py +427 -0
  22. edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
  23. edsl/caching/exceptions.py +24 -0
  24. edsl/caching/orm.py +30 -0
  25. edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
  26. edsl/caching/sql_dict.py +441 -0
  27. edsl/config/__init__.py +8 -0
  28. edsl/config/config_class.py +177 -0
  29. edsl/config.py +4 -176
  30. edsl/conversation/Conversation.py +7 -7
  31. edsl/conversation/car_buying.py +4 -4
  32. edsl/conversation/chips.py +6 -6
  33. edsl/coop/__init__.py +25 -2
  34. edsl/coop/coop.py +311 -75
  35. edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
  36. edsl/coop/exceptions.py +62 -0
  37. edsl/coop/price_fetcher.py +126 -0
  38. edsl/coop/utils.py +89 -24
  39. edsl/data_transfer_models.py +5 -72
  40. edsl/dataset/__init__.py +10 -0
  41. edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
  42. edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
  43. edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
  44. edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
  45. edsl/{results → dataset/display}/table_renderers.py +58 -2
  46. edsl/{results → dataset}/file_exports.py +4 -5
  47. edsl/{results → dataset}/smart_objects.py +2 -2
  48. edsl/enums.py +5 -205
  49. edsl/inference_services/__init__.py +5 -0
  50. edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
  51. edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
  52. edsl/inference_services/data_structures.py +3 -2
  53. edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
  54. edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
  55. edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
  56. edsl/inference_services/registry.py +4 -41
  57. edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
  58. edsl/inference_services/services/__init__.py +31 -0
  59. edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
  60. edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
  61. edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
  62. edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
  63. edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
  64. edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
  65. edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
  66. edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
  67. edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
  68. edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
  69. edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
  70. edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
  71. edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
  72. edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
  73. edsl/inference_services/write_available.py +1 -2
  74. edsl/instructions/__init__.py +6 -0
  75. edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
  76. edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
  77. edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
  78. edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
  79. edsl/interviews/__init__.py +4 -0
  80. edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
  81. edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
  82. edsl/interviews/interview.py +638 -0
  83. edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
  84. edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
  85. edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
  86. edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
  87. edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
  88. edsl/invigilators/__init__.py +38 -0
  89. edsl/invigilators/invigilator_base.py +477 -0
  90. edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
  91. edsl/invigilators/prompt_constructor.py +476 -0
  92. edsl/{agents → invigilators}/prompt_helpers.py +2 -1
  93. edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
  94. edsl/{agents → invigilators}/question_option_processor.py +96 -21
  95. edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
  96. edsl/jobs/__init__.py +7 -1
  97. edsl/jobs/async_interview_runner.py +99 -35
  98. edsl/jobs/check_survey_scenario_compatibility.py +7 -5
  99. edsl/jobs/data_structures.py +153 -22
  100. edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
  101. edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
  102. edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
  103. edsl/jobs/{Jobs.py → jobs.py} +313 -167
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
  106. edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
  107. edsl/jobs/jobs_pricing_estimation.py +347 -0
  108. edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
  109. edsl/jobs/jobs_runner_asyncio.py +282 -0
  110. edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
  111. edsl/jobs/results_exceptions_handler.py +2 -2
  112. edsl/key_management/__init__.py +28 -0
  113. edsl/key_management/key_lookup.py +161 -0
  114. edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
  115. edsl/key_management/key_lookup_collection.py +82 -0
  116. edsl/key_management/models.py +218 -0
  117. edsl/language_models/__init__.py +7 -2
  118. edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
  119. edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
  120. edsl/language_models/language_model.py +1080 -0
  121. edsl/language_models/model.py +10 -25
  122. edsl/language_models/{ModelList.py → model_list.py} +9 -14
  123. edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
  124. edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
  125. edsl/language_models/repair.py +4 -4
  126. edsl/language_models/utilities.py +4 -4
  127. edsl/notebooks/__init__.py +3 -1
  128. edsl/notebooks/{Notebook.py → notebook.py} +7 -8
  129. edsl/prompts/__init__.py +1 -1
  130. edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
  131. edsl/prompts/{Prompt.py → prompt.py} +101 -95
  132. edsl/questions/HTMLQuestion.py +1 -1
  133. edsl/questions/__init__.py +154 -25
  134. edsl/questions/answer_validator_mixin.py +1 -1
  135. edsl/questions/compose_questions.py +4 -3
  136. edsl/questions/derived/question_likert_five.py +166 -0
  137. edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
  138. edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
  139. edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
  140. edsl/questions/descriptors.py +24 -30
  141. edsl/questions/loop_processor.py +65 -19
  142. edsl/questions/question_base.py +881 -0
  143. edsl/questions/question_base_gen_mixin.py +15 -16
  144. edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
  145. edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
  146. edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
  147. edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
  148. edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
  149. edsl/questions/question_free_text.py +282 -0
  150. edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
  151. edsl/questions/{QuestionList.py → question_list.py} +6 -7
  152. edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
  153. edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
  154. edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
  155. edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
  156. edsl/questions/question_registry.py +4 -9
  157. edsl/questions/register_questions_meta.py +8 -4
  158. edsl/questions/response_validator_abc.py +17 -16
  159. edsl/results/__init__.py +4 -1
  160. edsl/{exceptions/results.py → results/exceptions.py} +1 -1
  161. edsl/results/report.py +197 -0
  162. edsl/results/{Result.py → result.py} +131 -45
  163. edsl/results/{Results.py → results.py} +365 -220
  164. edsl/results/results_selector.py +344 -25
  165. edsl/scenarios/__init__.py +30 -3
  166. edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
  167. edsl/scenarios/directory_scanner.py +156 -13
  168. edsl/scenarios/document_chunker.py +186 -0
  169. edsl/scenarios/exceptions.py +101 -0
  170. edsl/scenarios/file_methods.py +2 -3
  171. edsl/scenarios/{FileStore.py → file_store.py} +275 -189
  172. edsl/scenarios/handlers/__init__.py +14 -14
  173. edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
  174. edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
  175. edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
  176. edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
  177. edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
  178. edsl/scenarios/handlers/latex_file_store.py +5 -0
  179. edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
  180. edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
  181. edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
  182. edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
  183. edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
  184. edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
  185. edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
  186. edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
  187. edsl/scenarios/scenario.py +928 -0
  188. edsl/scenarios/scenario_join.py +18 -5
  189. edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
  190. edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
  191. edsl/scenarios/scenario_selector.py +5 -1
  192. edsl/study/ObjectEntry.py +2 -2
  193. edsl/study/SnapShot.py +5 -5
  194. edsl/study/Study.py +18 -19
  195. edsl/study/__init__.py +6 -4
  196. edsl/surveys/__init__.py +7 -4
  197. edsl/surveys/dag/__init__.py +2 -0
  198. edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
  199. edsl/surveys/{DAG.py → dag/dag.py} +13 -10
  200. edsl/surveys/descriptors.py +1 -1
  201. edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
  202. edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
  203. edsl/surveys/memory/__init__.py +3 -0
  204. edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
  205. edsl/surveys/rules/__init__.py +3 -0
  206. edsl/surveys/{Rule.py → rules/rule.py} +103 -43
  207. edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
  208. edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
  209. edsl/surveys/survey.py +1743 -0
  210. edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
  211. edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
  212. edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
  213. edsl/tasks/__init__.py +32 -0
  214. edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
  215. edsl/tasks/task_creators.py +135 -0
  216. edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
  217. edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
  218. edsl/tasks/task_status_log.py +85 -0
  219. edsl/tokens/__init__.py +2 -0
  220. edsl/tokens/interview_token_usage.py +53 -0
  221. edsl/utilities/PrettyList.py +1 -1
  222. edsl/utilities/SystemInfo.py +25 -22
  223. edsl/utilities/__init__.py +29 -21
  224. edsl/utilities/gcp_bucket/__init__.py +2 -0
  225. edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
  226. edsl/utilities/interface.py +44 -536
  227. edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
  228. edsl/utilities/repair_functions.py +1 -1
  229. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/METADATA +1 -1
  230. edsl-0.1.49.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -493
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/coop/PriceFetcher.py +0 -54
  236. edsl/data/Cache.py +0 -582
  237. edsl/data/CacheEntry.py +0 -238
  238. edsl/data/SQLiteDict.py +0 -292
  239. edsl/data/__init__.py +0 -5
  240. edsl/data/orm.py +0 -10
  241. edsl/exceptions/cache.py +0 -5
  242. edsl/exceptions/coop.py +0 -14
  243. edsl/exceptions/data.py +0 -14
  244. edsl/exceptions/scenarios.py +0 -29
  245. edsl/jobs/Answers.py +0 -43
  246. edsl/jobs/JobsPrompts.py +0 -354
  247. edsl/jobs/buckets/BucketCollection.py +0 -134
  248. edsl/jobs/buckets/ModelBuckets.py +0 -65
  249. edsl/jobs/buckets/TokenBucket.py +0 -283
  250. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  251. edsl/jobs/interviews/Interview.py +0 -395
  252. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  253. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  254. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  255. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  256. edsl/jobs/tasks/TaskCreators.py +0 -64
  257. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  258. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  259. edsl/language_models/LanguageModel.py +0 -635
  260. edsl/language_models/ServiceDataSources.py +0 -0
  261. edsl/language_models/key_management/KeyLookup.py +0 -63
  262. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  263. edsl/language_models/key_management/models.py +0 -137
  264. edsl/questions/QuestionBase.py +0 -544
  265. edsl/questions/QuestionFreeText.py +0 -130
  266. edsl/questions/derived/QuestionLikertFive.py +0 -76
  267. edsl/results/ResultsExportMixin.py +0 -45
  268. edsl/results/TextEditor.py +0 -50
  269. edsl/results/results_fetch_mixin.py +0 -33
  270. edsl/results/results_tools_mixin.py +0 -98
  271. edsl/scenarios/DocumentChunker.py +0 -104
  272. edsl/scenarios/Scenario.py +0 -548
  273. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  274. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  275. edsl/scenarios/handlers/latex.py +0 -5
  276. edsl/shared.py +0 -1
  277. edsl/surveys/Survey.py +0 -1301
  278. edsl/surveys/SurveyQualtricsImport.py +0 -284
  279. edsl/surveys/SurveyToApp.py +0 -141
  280. edsl/surveys/instructions/__init__.py +0 -0
  281. edsl/tools/__init__.py +0 -1
  282. edsl/tools/clusters.py +0 -192
  283. edsl/tools/embeddings.py +0 -27
  284. edsl/tools/embeddings_plotting.py +0 -118
  285. edsl/tools/plotting.py +0 -112
  286. edsl/tools/summarize.py +0 -18
  287. edsl/utilities/data/Registry.py +0 -6
  288. edsl/utilities/data/__init__.py +0 -1
  289. edsl/utilities/data/scooter_results.json +0 -1
  290. edsl-0.1.47.dist-info/RECORD +0 -354
  291. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  292. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  293. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  294. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  295. /edsl/{results → dataset/display}/table_display.css +0 -0
  296. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  297. /edsl/{results → dataset}/tree_explore.py +0 -0
  298. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  299. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  300. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  301. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  302. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  303. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  304. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  305. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  306. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  307. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  308. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  309. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  310. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  311. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  312. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  313. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/WHEEL +0 -0
@@ -0,0 +1,427 @@
1
+ """
2
+ CacheEntry implementation for the EDSL data caching system.
3
+
4
+ This module provides the CacheEntry class, which represents a single cached
5
+ language model response along with its associated metadata. Cache entries are
6
+ uniquely identified by a hash of their key fields, making it efficient to
7
+ store and retrieve responses for identical prompts.
8
+ """
9
+
10
+ from __future__ import annotations
11
+ import json
12
+ import datetime
13
+ import hashlib
14
+ from typing import Optional, Dict, List, Any, Union
15
+ from uuid import uuid4
16
+
17
+ from ..base import RepresentationMixin
18
+
19
+
20
+ class CacheEntry(RepresentationMixin):
21
+ """
22
+ Represents a single cached language model response with associated metadata.
23
+
24
+ CacheEntry objects store language model responses along with the prompts and
25
+ parameters that generated them. Each entry is uniquely identified by a hash
26
+ of its key fields (model, parameters, prompts, and iteration), making it
27
+ possible to efficiently retrieve cached responses for identical inputs.
28
+
29
+ Attributes:
30
+ model (str): The language model identifier (e.g., "gpt-3.5-turbo")
31
+ parameters (dict): Model parameters used for generation (e.g., temperature)
32
+ system_prompt (str): The system prompt provided to the model
33
+ user_prompt (str): The user prompt provided to the model
34
+ output (str): The generated response from the language model
35
+ iteration (int): Iteration number, for when multiple outputs are generated
36
+ with the same prompts (defaults to 0)
37
+ timestamp (int): Unix timestamp when the entry was created
38
+ service (str, optional): The service provider for the model (e.g., "openai")
39
+
40
+ Class Attributes:
41
+ key_fields (List[str]): Fields used to generate the unique hash key
42
+ all_fields (List[str]): All fields stored in the cache entry
43
+ """
44
+
45
+ key_fields = ["model", "parameters", "system_prompt", "user_prompt", "iteration"]
46
+ all_fields = key_fields + ["timestamp", "output", "service"]
47
+
48
+ def __init__(
49
+ self,
50
+ *,
51
+ model: str,
52
+ parameters: dict,
53
+ system_prompt: str,
54
+ user_prompt: str,
55
+ iteration: Optional[int] = None,
56
+ output: str,
57
+ timestamp: Optional[int] = None,
58
+ service: Optional[str] = None,
59
+ ):
60
+ self.model = model
61
+ self.parameters = parameters
62
+ self.system_prompt = system_prompt
63
+ self.user_prompt = user_prompt
64
+ self.output = output
65
+ self.iteration = iteration or 0
66
+ self.timestamp = timestamp or int(
67
+ datetime.datetime.now(datetime.timezone.utc).timestamp()
68
+ )
69
+ self.service = service
70
+ self._check_types()
71
+
72
+ def _check_types(self) -> None:
73
+ """
74
+ Validates that all attributes have the correct types.
75
+
76
+ This method is called during initialization to ensure that all
77
+ attributes have the expected types, raising TypeError exceptions
78
+ with descriptive messages when validation fails.
79
+
80
+ Raises:
81
+ TypeError: If any attribute has an incorrect type
82
+ """
83
+ if not isinstance(self.model, str):
84
+ raise TypeError("`model` should be a string.")
85
+ if not isinstance(self.parameters, dict):
86
+ raise TypeError("`parameters` should be a dictionary.")
87
+ if not isinstance(self.system_prompt, str):
88
+ raise TypeError("`system_prompt` should be a string.")
89
+ if not isinstance(self.user_prompt, str):
90
+ raise TypeError("`user_prompt` should be a string")
91
+ if not isinstance(self.output, str):
92
+ raise TypeError("`output` should be a string")
93
+ if not isinstance(self.iteration, int):
94
+ raise TypeError("`iteration` should be an integer")
95
+ # Note: timestamp is stored as int for compatibility, but could be float in future
96
+ if not isinstance(self.timestamp, int):
97
+ raise TypeError(f"`timestamp` should be an integer")
98
+ if self.service is not None and not isinstance(self.service, str):
99
+ raise TypeError("`service` should be either a string or None")
100
+
101
+ @classmethod
102
+ def gen_key(
103
+ cls, *, model: str, parameters: Dict[str, Any],
104
+ system_prompt: str, user_prompt: str, iteration: int
105
+ ) -> str:
106
+ """
107
+ Generates a unique key hash for the cache entry based on input parameters.
108
+
109
+ This method creates a deterministic hash key by concatenating the model name,
110
+ parameters (sorted to ensure consistency), system prompt, user prompt, and
111
+ iteration number. The hash enables efficient lookup of cache entries with
112
+ identical inputs.
113
+
114
+ Args:
115
+ model: The language model identifier
116
+ parameters: Dictionary of model parameters (will be sorted for consistency)
117
+ system_prompt: The system prompt provided to the model
118
+ user_prompt: The user prompt provided to the model
119
+ iteration: Iteration number for this combination of inputs
120
+
121
+ Returns:
122
+ A hex-encoded MD5 hash string that uniquely identifies this combination
123
+ of inputs
124
+
125
+ Note:
126
+ - The hash treats single and double quotes as equivalent
127
+ - Parameters are sorted to ensure consistent hashing regardless of order
128
+ """
129
+ long_key = f"{model}{json.dumps(parameters, sort_keys=True)}{system_prompt}{user_prompt}{iteration}"
130
+ return hashlib.md5(long_key.encode()).hexdigest()
131
+
132
+ @property
133
+ def key(self) -> str:
134
+ """
135
+ Returns the unique hash key for this cache entry.
136
+
137
+ This property extracts the key fields from the instance and generates
138
+ a hash key using the gen_key classmethod. The key uniquely identifies
139
+ this combination of model, parameters, prompts, and iteration.
140
+
141
+ Returns:
142
+ A hex-encoded MD5 hash string that uniquely identifies this cache entry
143
+ """
144
+ d = {k: value for k, value in self.__dict__.items() if k in self.key_fields}
145
+ return self.gen_key(**d)
146
+
147
+ def to_dict(self, add_edsl_version: bool = True) -> Dict[str, Any]:
148
+ """
149
+ Converts the cache entry to a dictionary representation.
150
+
151
+ This method creates a dictionary containing all fields of the cache entry,
152
+ making it suitable for serialization or storage.
153
+
154
+ Args:
155
+ add_edsl_version: If True, adds EDSL version information to the dict
156
+ (Currently disabled pending implementation)
157
+
158
+ Returns:
159
+ A dictionary representation of the cache entry with all fields
160
+
161
+ Note:
162
+ The edsl_version feature is currently disabled in the implementation
163
+ """
164
+ d = {
165
+ "model": self.model,
166
+ "parameters": self.parameters,
167
+ "system_prompt": self.system_prompt,
168
+ "user_prompt": self.user_prompt,
169
+ "output": self.output,
170
+ "iteration": self.iteration,
171
+ "timestamp": self.timestamp,
172
+ "service": self.service,
173
+ }
174
+ # Feature for adding version information (currently disabled)
175
+ # if add_edsl_version:
176
+ # from edsl import __version__
177
+ # d["edsl_version"] = __version__
178
+ # d["edsl_class_name"] = self.__class__.__name__
179
+ return d
180
+
181
+ def keys(self) -> List[str]:
182
+ """
183
+ Returns a list of field names in this cache entry.
184
+
185
+ This method enables dict-like access to cache entry field names.
186
+
187
+ Returns:
188
+ List of field names from the dictionary representation
189
+ """
190
+ return list(self.to_dict().keys())
191
+
192
+ def values(self) -> List[Any]:
193
+ """
194
+ Returns a list of values for all fields in this cache entry.
195
+
196
+ This method enables dict-like access to cache entry values.
197
+
198
+ Returns:
199
+ List of values from the dictionary representation
200
+ """
201
+ return list(self.to_dict().values())
202
+
203
+ def __getitem__(self, key: str) -> Any:
204
+ """
205
+ Enables dictionary-style access to cache entry attributes.
206
+
207
+ This method allows accessing cache entry attributes using dictionary
208
+ syntax (e.g., entry["model"] instead of entry.model).
209
+
210
+ Args:
211
+ key: The name of the attribute to access
212
+
213
+ Returns:
214
+ The value of the specified attribute
215
+
216
+ Raises:
217
+ AttributeError: If the specified attribute doesn't exist
218
+ """
219
+ return getattr(self, key)
220
+
221
+ @classmethod
222
+ def from_dict(cls, data: Dict[str, Any]) -> CacheEntry:
223
+ """
224
+ Creates a CacheEntry object from a dictionary representation.
225
+
226
+ This factory method enables reconstruction of CacheEntry objects
227
+ from serialized dictionary representations, such as those produced
228
+ by the to_dict method.
229
+
230
+ Args:
231
+ data: Dictionary containing required CacheEntry fields
232
+
233
+ Returns:
234
+ A new CacheEntry instance with fields populated from the dictionary
235
+
236
+ Raises:
237
+ TypeError: If data contains fields with incorrect types
238
+ KeyError: If required fields are missing from data
239
+ """
240
+ return cls(**data)
241
+
242
+ def __eq__(self, other: Any) -> bool:
243
+ """
244
+ Compares this cache entry with another for equality.
245
+
246
+ This method checks if all fields except timestamp are equal between
247
+ this cache entry and another. The timestamp is excluded from the
248
+ comparison because it's typically not relevant for determining if
249
+ two entries represent the same cached response.
250
+
251
+ Args:
252
+ other: Another object to compare with this cache entry
253
+
254
+ Returns:
255
+ True if all fields except timestamp are equal, False otherwise
256
+
257
+ Note:
258
+ Returns False if other is not a CacheEntry instance
259
+ """
260
+ if not isinstance(other, CacheEntry):
261
+ return False
262
+ for field in self.all_fields:
263
+ if getattr(self, field) != getattr(other, field) and field != "timestamp":
264
+ return False
265
+ return True
266
+
267
+ def __repr__(self) -> str:
268
+ """
269
+ Returns a string representation of this cache entry.
270
+
271
+ This method creates a string representation that displays all fields
272
+ of the cache entry in a format that can be evaluated to recreate
273
+ the object.
274
+
275
+ Returns:
276
+ A string representation that can be passed to eval() to recreate
277
+ this cache entry
278
+ """
279
+ return (
280
+ f"CacheEntry(model={repr(self.model)}, "
281
+ f"parameters={self.parameters}, "
282
+ f"system_prompt={repr(self.system_prompt)}, "
283
+ f"user_prompt={repr(self.user_prompt)}, "
284
+ f"output={repr(self.output)}, "
285
+ f"iteration={self.iteration}, "
286
+ f"timestamp={self.timestamp}, "
287
+ f"service={repr(self.service)})"
288
+ )
289
+
290
+ @classmethod
291
+ def example(cls, randomize: bool = False) -> CacheEntry:
292
+ """
293
+ Creates an example CacheEntry instance for testing and demonstration.
294
+
295
+ This factory method generates a pre-populated CacheEntry with example
296
+ values, useful for testing, documentation, and examples.
297
+
298
+ Args:
299
+ randomize: If True, adds a random UUID to the system prompt to make
300
+ the entry unique and generate a different hash key
301
+
302
+ Returns:
303
+ A fully populated example CacheEntry instance
304
+
305
+ Example:
306
+ >>> entry = CacheEntry.example()
307
+ >>> isinstance(entry, CacheEntry)
308
+ True
309
+ >>> entry.model
310
+ 'gpt-3.5-turbo'
311
+ """
312
+ addition = "" if not randomize else str(uuid4())
313
+ return CacheEntry(
314
+ model="gpt-3.5-turbo",
315
+ parameters={"temperature": 0.5},
316
+ system_prompt=f"The quick brown fox jumps over the lazy dog.{addition}",
317
+ user_prompt="What does the fox say?",
318
+ output="The fox says 'hello'",
319
+ iteration=1,
320
+ timestamp=int(datetime.datetime.now(datetime.timezone.utc).timestamp()),
321
+ service="openai",
322
+ )
323
+
324
+ @classmethod
325
+ def example_dict(cls) -> Dict[str, CacheEntry]:
326
+ """
327
+ Creates an example dictionary mapping a key to a CacheEntry.
328
+
329
+ This method demonstrates how CacheEntry objects are typically stored
330
+ in a cache, with their hash keys as dictionary keys.
331
+
332
+ Returns:
333
+ A dictionary with a single entry mapping the example entry's key
334
+ to the example entry
335
+
336
+ Note:
337
+ This is particularly useful for testing and demonstrating the
338
+ Cache class functionality
339
+ """
340
+ cache_entry = cls.example()
341
+ return {cache_entry.key: cache_entry}
342
+
343
+ @classmethod
344
+ def fetch_input_example(cls) -> Dict[str, Any]:
345
+ """
346
+ Creates an example input dictionary for a 'fetch' operation.
347
+
348
+ This method generates a dictionary containing the fields needed to
349
+ look up a cache entry (everything except the response/output fields).
350
+
351
+ Returns:
352
+ A dictionary with fields needed to generate a cache key for lookup
353
+
354
+ Note:
355
+ This is used by the Cache class to demonstrate fetch operations
356
+ """
357
+ input = cls.example().to_dict()
358
+ _ = input.pop("timestamp")
359
+ _ = input.pop("output")
360
+ _ = input.pop("service")
361
+ return input
362
+
363
+ @classmethod
364
+ def store_input_example(cls) -> Dict[str, Any]:
365
+ """
366
+ Creates an example input dictionary for a 'store' operation.
367
+
368
+ This method generates a dictionary containing the fields needed to
369
+ store a new cache entry, with 'output' renamed to 'response' to match
370
+ the API of the Cache.store method.
371
+
372
+ Returns:
373
+ A dictionary with fields needed to store a new cache entry
374
+
375
+ Note:
376
+ This is used by the Cache class to demonstrate store operations
377
+ """
378
+ input = cls.example().to_dict()
379
+ _ = input.pop("timestamp")
380
+ input["response"] = input.pop("output")
381
+ return input
382
+
383
+
384
+ def main() -> None:
385
+ """
386
+ Demonstration of CacheEntry functionality for interactive testing.
387
+
388
+ This function demonstrates the key features of the CacheEntry class,
389
+ including creating entries, calculating hash keys, converting to/from
390
+ dictionaries, and comparing entries.
391
+
392
+ Note:
393
+ This function is intended to be run in an interactive Python session
394
+ for exploration and testing, not as part of normal code execution.
395
+ """
396
+ from .cache_entry import CacheEntry
397
+
398
+ # Create an example cache entry
399
+ cache_entry = CacheEntry.example()
400
+ print(f"Example cache entry: {cache_entry}")
401
+
402
+ # Demonstrate key generation
403
+ print(f"Cache key: {cache_entry.key}")
404
+
405
+ # Demonstrate serialization and deserialization
406
+ entry_dict = cache_entry.to_dict()
407
+ print(f"Dictionary representation: {entry_dict}")
408
+ reconstructed = CacheEntry.from_dict(entry_dict)
409
+ print(f"Reconstructed from dict: {reconstructed}")
410
+
411
+ # Demonstrate equality comparisons
412
+ print(f"Same content equals: {cache_entry == CacheEntry.example()}")
413
+ print(f"Same key equals: {cache_entry.key == CacheEntry.example().key}")
414
+
415
+ # Demonstrate repr evaluation
416
+ print(f"Repr can be evaluated: {eval(repr(cache_entry)) == cache_entry}")
417
+
418
+ # Demonstrate utility methods
419
+ print(f"Example dict: {CacheEntry.example_dict()}")
420
+ print(f"Fetch input example: {CacheEntry.fetch_input_example()}")
421
+ print(f"Store input example: {CacheEntry.store_input_example()}")
422
+
423
+
424
+ if __name__ == "__main__":
425
+ import doctest
426
+
427
+ doctest.testmod()
@@ -6,15 +6,14 @@ import shutil
6
6
  from typing import TYPE_CHECKING
7
7
 
8
8
  if TYPE_CHECKING:
9
- from edsl.data.Cache import Cache
10
- from edsl.data.CacheEntry import CacheEntry
11
-
9
+ from .cache import Cache
10
+ from .cache_entry import CacheEntry
12
11
 
13
12
  def set_session_cache(cache: "Cache") -> None:
14
13
  """
15
14
  Set the session cache.
16
15
  """
17
- from edsl.config import CONFIG
16
+ from ..config import CONFIG
18
17
 
19
18
  CONFIG.EDSL_SESSION_CACHE = cache
20
19
 
@@ -23,7 +22,7 @@ def unset_session_cache() -> None:
23
22
  """
24
23
  Unset the session cache.
25
24
  """
26
- from edsl.config import CONFIG
25
+ from ..config import CONFIG
27
26
 
28
27
  if hasattr(CONFIG, "EDSL_SESSION_CACHE"):
29
28
  del CONFIG.EDSL_SESSION_CACHE
@@ -32,11 +31,14 @@ def unset_session_cache() -> None:
32
31
  class CacheHandler:
33
32
  """
34
33
  This CacheHandler figures out what caches are available and does migrations, as needed.
34
+
35
+ >>> cache_handler = CacheHandler()
36
+ >>> c = cache_handler.get_cache()
35
37
  """
36
38
 
37
39
  @property
38
40
  def CACHE_PATH(self):
39
- from edsl.config import CONFIG
41
+ from ..config import CONFIG
40
42
 
41
43
  return CONFIG.get("EDSL_DATABASE_PATH")
42
44
 
@@ -62,17 +64,17 @@ class CacheHandler:
62
64
  """
63
65
  Generate a Cache object.
64
66
  """
65
- from edsl.data.Cache import Cache
67
+ from .cache import Cache
66
68
 
67
69
  if self.test:
68
70
  return Cache(data={})
69
71
 
70
- from edsl.config import CONFIG
72
+ from ..config import CONFIG
71
73
 
72
74
  if hasattr(CONFIG, "EDSL_SESSION_CACHE"):
73
75
  return CONFIG.EDSL_SESSION_CACHE
74
76
 
75
- from edsl.data.SQLiteDict import SQLiteDict
77
+ from .sql_dict import SQLiteDict
76
78
 
77
79
  cache = Cache(data=SQLiteDict(self.CACHE_PATH))
78
80
  return cache
@@ -114,15 +116,13 @@ class CacheHandler:
114
116
  return old_data
115
117
 
116
118
  def _parse_old_cache_entry(self, row: tuple, schema) -> CacheEntry:
117
- """
118
- Parse an old cache entry.
119
- """
119
+ """Parse an old cache entry."""
120
120
  entry_dict = {k: row[i] for i, k in enumerate(schema.keys())}
121
121
  _ = entry_dict.pop("id")
122
122
  entry_dict["user_prompt"] = entry_dict.pop("prompt")
123
123
  parameters = entry_dict["parameters"]
124
124
  entry_dict["parameters"] = ast.literal_eval(parameters)
125
- from edsl.data.CacheEntry import CacheEntry
125
+ from .cache_entry import CacheEntry
126
126
 
127
127
  entry = CacheEntry(**entry_dict)
128
128
  return entry
@@ -137,6 +137,7 @@ class CacheHandler:
137
137
  """
138
138
  Read in a new-style sqlite cache and return a dictionary of dictionaries.
139
139
  """
140
+ import sqlite3
140
141
  conn = sqlite3.connect(uri)
141
142
  with conn:
142
143
  cur = conn.cursor()
@@ -162,7 +163,5 @@ class CacheHandler:
162
163
 
163
164
 
164
165
  if __name__ == "__main__":
165
- # ch = CacheHandler()
166
166
  import doctest
167
-
168
167
  doctest.testmod()
@@ -0,0 +1,24 @@
1
+ """
2
+ Custom exceptions for the data module in the EDSL framework.
3
+
4
+ This module defines exceptions that are raised during cache operations,
5
+ such as when entries cannot be stored, retrieved, or synchronized.
6
+ """
7
+
8
+ from ..base import BaseException
9
+
10
+
11
+ class CacheError(BaseException):
12
+ """
13
+ Exception raised for errors related to cache operations.
14
+
15
+ This exception is raised when cache operations fail, such as when:
16
+ - A cache key cannot be generated
17
+ - A cache entry cannot be stored or retrieved
18
+ - A cache synchronization operation fails
19
+ - Cache migration encounters an error
20
+
21
+ Attributes:
22
+ message (str): Explanation of the error
23
+ """
24
+ relevant_doc = "https://docs.expectedparrot.com/en/latest/data.html#cache"
edsl/caching/orm.py ADDED
@@ -0,0 +1,30 @@
1
+ """
2
+ SQLAlchemy ORM definitions for EDSL data persistence.
3
+
4
+ This module defines the SQLAlchemy ORM models used for storing cache data
5
+ in a SQL database. It provides a simple key-value schema that allows
6
+ for efficient storage and retrieval of cached data.
7
+ """
8
+
9
+ from sqlalchemy import Column, String
10
+ from sqlalchemy.ext.declarative import declarative_base
11
+
12
+ Base = declarative_base()
13
+
14
+
15
+ class Data(Base):
16
+ """
17
+ SQLAlchemy ORM model for key-value data storage.
18
+
19
+ This class represents a table in the SQL database with a simple
20
+ key-value schema. It is used by the Cache and SQLiteDict classes
21
+ to store cached data persistently.
22
+
23
+ Attributes:
24
+ __tablename__ (str): Name of the database table ("data")
25
+ key (Column): Primary key column for storing lookup keys
26
+ value (Column): Column for storing serialized data values
27
+ """
28
+ __tablename__ = "data"
29
+ key = Column(String, primary_key=True)
30
+ value = Column(String)
@@ -4,9 +4,9 @@ from contextlib import AbstractContextManager
4
4
  from collections import UserList
5
5
 
6
6
  if TYPE_CHECKING:
7
- from .Cache import Cache
7
+ from .cache import Cache
8
8
  from edsl.coop.coop import Coop
9
- from .CacheEntry import CacheEntry
9
+ from .cache_entry import CacheEntry
10
10
 
11
11
  from logging import Logger
12
12
 
@@ -35,7 +35,7 @@ class CacheEntriesList(UserList):
35
35
  return f"CacheEntries({entries_repr})"
36
36
 
37
37
  def to_cache(self) -> "Cache":
38
- from edsl.data.Cache import Cache
38
+ from edsl.caching.cache import Cache
39
39
 
40
40
  return Cache({entry.key: entry for entry in self.data})
41
41