edsl 0.1.39.dev2__py3-none-any.whl → 0.1.39.dev3__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 (334) hide show
  1. edsl/Base.py +332 -385
  2. edsl/BaseDiff.py +260 -260
  3. edsl/TemplateLoader.py +24 -24
  4. edsl/__init__.py +49 -57
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +867 -1079
  7. edsl/agents/AgentList.py +413 -551
  8. edsl/agents/Invigilator.py +233 -285
  9. edsl/agents/InvigilatorBase.py +270 -254
  10. edsl/agents/PromptConstructor.py +354 -252
  11. edsl/agents/__init__.py +3 -2
  12. edsl/agents/descriptors.py +99 -99
  13. edsl/agents/prompt_helpers.py +129 -129
  14. edsl/auto/AutoStudy.py +117 -117
  15. edsl/auto/StageBase.py +230 -230
  16. edsl/auto/StageGenerateSurvey.py +178 -178
  17. edsl/auto/StageLabelQuestions.py +125 -125
  18. edsl/auto/StagePersona.py +61 -61
  19. edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
  20. edsl/auto/StagePersonaDimensionValues.py +74 -74
  21. edsl/auto/StagePersonaDimensions.py +69 -69
  22. edsl/auto/StageQuestions.py +73 -73
  23. edsl/auto/SurveyCreatorPipeline.py +21 -21
  24. edsl/auto/utilities.py +224 -224
  25. edsl/base/Base.py +279 -279
  26. edsl/config.py +157 -177
  27. edsl/conversation/Conversation.py +290 -290
  28. edsl/conversation/car_buying.py +58 -59
  29. edsl/conversation/chips.py +95 -95
  30. edsl/conversation/mug_negotiation.py +81 -81
  31. edsl/conversation/next_speaker_utilities.py +93 -93
  32. edsl/coop/PriceFetcher.py +54 -54
  33. edsl/coop/__init__.py +2 -2
  34. edsl/coop/coop.py +1028 -1090
  35. edsl/coop/utils.py +131 -131
  36. edsl/data/Cache.py +555 -562
  37. edsl/data/CacheEntry.py +233 -230
  38. edsl/data/CacheHandler.py +149 -170
  39. edsl/data/RemoteCacheSync.py +78 -78
  40. edsl/data/SQLiteDict.py +292 -292
  41. edsl/data/__init__.py +4 -5
  42. edsl/data/orm.py +10 -10
  43. edsl/data_transfer_models.py +73 -74
  44. edsl/enums.py +175 -195
  45. edsl/exceptions/BaseException.py +21 -21
  46. edsl/exceptions/__init__.py +54 -54
  47. edsl/exceptions/agents.py +42 -54
  48. edsl/exceptions/cache.py +5 -5
  49. edsl/exceptions/configuration.py +16 -16
  50. edsl/exceptions/coop.py +10 -10
  51. edsl/exceptions/data.py +14 -14
  52. edsl/exceptions/general.py +34 -34
  53. edsl/exceptions/jobs.py +33 -33
  54. edsl/exceptions/language_models.py +63 -63
  55. edsl/exceptions/prompts.py +15 -15
  56. edsl/exceptions/questions.py +91 -109
  57. edsl/exceptions/results.py +29 -29
  58. edsl/exceptions/scenarios.py +22 -29
  59. edsl/exceptions/surveys.py +37 -37
  60. edsl/inference_services/AnthropicService.py +87 -84
  61. edsl/inference_services/AwsBedrock.py +120 -118
  62. edsl/inference_services/AzureAI.py +217 -215
  63. edsl/inference_services/DeepInfraService.py +18 -18
  64. edsl/inference_services/GoogleService.py +148 -139
  65. edsl/inference_services/GroqService.py +20 -20
  66. edsl/inference_services/InferenceServiceABC.py +147 -80
  67. edsl/inference_services/InferenceServicesCollection.py +97 -122
  68. edsl/inference_services/MistralAIService.py +123 -120
  69. edsl/inference_services/OllamaService.py +18 -18
  70. edsl/inference_services/OpenAIService.py +224 -221
  71. edsl/inference_services/PerplexityService.py +163 -160
  72. edsl/inference_services/TestService.py +89 -92
  73. edsl/inference_services/TogetherAIService.py +170 -170
  74. edsl/inference_services/models_available_cache.py +118 -118
  75. edsl/inference_services/rate_limits_cache.py +25 -25
  76. edsl/inference_services/registry.py +41 -41
  77. edsl/inference_services/write_available.py +10 -10
  78. edsl/jobs/Answers.py +56 -43
  79. edsl/jobs/Jobs.py +898 -757
  80. edsl/jobs/JobsChecks.py +147 -172
  81. edsl/jobs/JobsPrompts.py +268 -270
  82. edsl/jobs/JobsRemoteInferenceHandler.py +239 -287
  83. edsl/jobs/__init__.py +1 -1
  84. edsl/jobs/buckets/BucketCollection.py +63 -104
  85. edsl/jobs/buckets/ModelBuckets.py +65 -65
  86. edsl/jobs/buckets/TokenBucket.py +251 -283
  87. edsl/jobs/interviews/Interview.py +661 -358
  88. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
  89. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
  90. edsl/jobs/interviews/InterviewStatistic.py +63 -63
  91. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
  92. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
  93. edsl/jobs/interviews/InterviewStatusLog.py +92 -92
  94. edsl/jobs/interviews/ReportErrors.py +66 -66
  95. edsl/jobs/interviews/interview_status_enum.py +9 -9
  96. edsl/jobs/runners/JobsRunnerAsyncio.py +466 -421
  97. edsl/jobs/runners/JobsRunnerStatus.py +330 -330
  98. edsl/jobs/tasks/QuestionTaskCreator.py +242 -244
  99. edsl/jobs/tasks/TaskCreators.py +64 -64
  100. edsl/jobs/tasks/TaskHistory.py +450 -449
  101. edsl/jobs/tasks/TaskStatusLog.py +23 -23
  102. edsl/jobs/tasks/task_status_enum.py +163 -161
  103. edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
  104. edsl/jobs/tokens/TokenUsage.py +34 -34
  105. edsl/language_models/KeyLookup.py +30 -0
  106. edsl/language_models/LanguageModel.py +668 -571
  107. edsl/language_models/ModelList.py +155 -153
  108. edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
  109. edsl/language_models/__init__.py +3 -2
  110. edsl/language_models/fake_openai_call.py +15 -15
  111. edsl/language_models/fake_openai_service.py +61 -61
  112. edsl/language_models/registry.py +190 -180
  113. edsl/language_models/repair.py +156 -156
  114. edsl/language_models/unused/ReplicateBase.py +83 -0
  115. edsl/language_models/utilities.py +64 -65
  116. edsl/notebooks/Notebook.py +258 -263
  117. edsl/notebooks/__init__.py +1 -1
  118. edsl/prompts/Prompt.py +362 -352
  119. edsl/prompts/__init__.py +2 -2
  120. edsl/questions/AnswerValidatorMixin.py +289 -334
  121. edsl/questions/QuestionBase.py +664 -509
  122. edsl/questions/QuestionBaseGenMixin.py +161 -165
  123. edsl/questions/QuestionBasePromptsMixin.py +217 -221
  124. edsl/questions/QuestionBudget.py +227 -227
  125. edsl/questions/QuestionCheckBox.py +359 -359
  126. edsl/questions/QuestionExtract.py +182 -182
  127. edsl/questions/QuestionFreeText.py +114 -113
  128. edsl/questions/QuestionFunctional.py +166 -166
  129. edsl/questions/QuestionList.py +231 -229
  130. edsl/questions/QuestionMultipleChoice.py +286 -330
  131. edsl/questions/QuestionNumerical.py +153 -151
  132. edsl/questions/QuestionRank.py +324 -314
  133. edsl/questions/Quick.py +41 -41
  134. edsl/questions/RegisterQuestionsMeta.py +71 -71
  135. edsl/questions/ResponseValidatorABC.py +174 -200
  136. edsl/questions/SimpleAskMixin.py +73 -74
  137. edsl/questions/__init__.py +26 -27
  138. edsl/questions/compose_questions.py +98 -98
  139. edsl/questions/decorators.py +21 -21
  140. edsl/questions/derived/QuestionLikertFive.py +76 -76
  141. edsl/questions/derived/QuestionLinearScale.py +87 -90
  142. edsl/questions/derived/QuestionTopK.py +93 -93
  143. edsl/questions/derived/QuestionYesNo.py +82 -82
  144. edsl/questions/descriptors.py +413 -427
  145. edsl/questions/prompt_templates/question_budget.jinja +13 -13
  146. edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
  147. edsl/questions/prompt_templates/question_extract.jinja +11 -11
  148. edsl/questions/prompt_templates/question_free_text.jinja +3 -3
  149. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
  150. edsl/questions/prompt_templates/question_list.jinja +17 -17
  151. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
  152. edsl/questions/prompt_templates/question_numerical.jinja +36 -36
  153. edsl/questions/question_registry.py +177 -177
  154. edsl/questions/settings.py +12 -12
  155. edsl/questions/templates/budget/answering_instructions.jinja +7 -7
  156. edsl/questions/templates/budget/question_presentation.jinja +7 -7
  157. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
  158. edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
  159. edsl/questions/templates/extract/answering_instructions.jinja +7 -7
  160. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
  161. edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
  162. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
  163. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
  164. edsl/questions/templates/list/answering_instructions.jinja +3 -3
  165. edsl/questions/templates/list/question_presentation.jinja +5 -5
  166. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
  167. edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
  168. edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
  169. edsl/questions/templates/numerical/question_presentation.jinja +6 -6
  170. edsl/questions/templates/rank/answering_instructions.jinja +11 -11
  171. edsl/questions/templates/rank/question_presentation.jinja +15 -15
  172. edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
  173. edsl/questions/templates/top_k/question_presentation.jinja +22 -22
  174. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
  175. edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
  176. edsl/results/CSSParameterizer.py +108 -108
  177. edsl/results/Dataset.py +424 -587
  178. edsl/results/DatasetExportMixin.py +731 -653
  179. edsl/results/DatasetTree.py +275 -295
  180. edsl/results/Result.py +465 -451
  181. edsl/results/Results.py +1165 -1172
  182. edsl/results/ResultsDBMixin.py +238 -0
  183. edsl/results/ResultsExportMixin.py +43 -45
  184. edsl/results/ResultsFetchMixin.py +33 -33
  185. edsl/results/ResultsGGMixin.py +121 -121
  186. edsl/results/ResultsToolsMixin.py +98 -98
  187. edsl/results/Selector.py +135 -145
  188. edsl/results/TableDisplay.py +198 -125
  189. edsl/results/__init__.py +2 -2
  190. edsl/results/table_display.css +77 -77
  191. edsl/results/tree_explore.py +115 -115
  192. edsl/scenarios/FileStore.py +632 -511
  193. edsl/scenarios/Scenario.py +601 -498
  194. edsl/scenarios/ScenarioHtmlMixin.py +64 -65
  195. edsl/scenarios/ScenarioJoin.py +127 -131
  196. edsl/scenarios/ScenarioList.py +1287 -1430
  197. edsl/scenarios/ScenarioListExportMixin.py +52 -45
  198. edsl/scenarios/ScenarioListPdfMixin.py +261 -239
  199. edsl/scenarios/__init__.py +4 -3
  200. edsl/shared.py +1 -1
  201. edsl/study/ObjectEntry.py +173 -173
  202. edsl/study/ProofOfWork.py +113 -113
  203. edsl/study/SnapShot.py +80 -80
  204. edsl/study/Study.py +528 -521
  205. edsl/study/__init__.py +4 -4
  206. edsl/surveys/DAG.py +148 -148
  207. edsl/surveys/Memory.py +31 -31
  208. edsl/surveys/MemoryPlan.py +244 -244
  209. edsl/surveys/Rule.py +326 -327
  210. edsl/surveys/RuleCollection.py +387 -385
  211. edsl/surveys/Survey.py +1801 -1229
  212. edsl/surveys/SurveyCSS.py +261 -273
  213. edsl/surveys/SurveyExportMixin.py +259 -259
  214. edsl/surveys/{SurveyFlowVisualization.py → SurveyFlowVisualizationMixin.py} +179 -181
  215. edsl/surveys/SurveyQualtricsImport.py +284 -284
  216. edsl/surveys/__init__.py +3 -5
  217. edsl/surveys/base.py +53 -53
  218. edsl/surveys/descriptors.py +56 -60
  219. edsl/surveys/instructions/ChangeInstruction.py +49 -48
  220. edsl/surveys/instructions/Instruction.py +65 -56
  221. edsl/surveys/instructions/InstructionCollection.py +77 -82
  222. edsl/templates/error_reporting/base.html +23 -23
  223. edsl/templates/error_reporting/exceptions_by_model.html +34 -34
  224. edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
  225. edsl/templates/error_reporting/exceptions_by_type.html +16 -16
  226. edsl/templates/error_reporting/interview_details.html +115 -115
  227. edsl/templates/error_reporting/interviews.html +19 -19
  228. edsl/templates/error_reporting/overview.html +4 -4
  229. edsl/templates/error_reporting/performance_plot.html +1 -1
  230. edsl/templates/error_reporting/report.css +73 -73
  231. edsl/templates/error_reporting/report.html +117 -117
  232. edsl/templates/error_reporting/report.js +25 -25
  233. edsl/tools/__init__.py +1 -1
  234. edsl/tools/clusters.py +192 -192
  235. edsl/tools/embeddings.py +27 -27
  236. edsl/tools/embeddings_plotting.py +118 -118
  237. edsl/tools/plotting.py +112 -112
  238. edsl/tools/summarize.py +18 -18
  239. edsl/utilities/SystemInfo.py +28 -28
  240. edsl/utilities/__init__.py +22 -22
  241. edsl/utilities/ast_utilities.py +25 -25
  242. edsl/utilities/data/Registry.py +6 -6
  243. edsl/utilities/data/__init__.py +1 -1
  244. edsl/utilities/data/scooter_results.json +1 -1
  245. edsl/utilities/decorators.py +77 -77
  246. edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
  247. edsl/utilities/interface.py +627 -627
  248. edsl/utilities/naming_utilities.py +263 -263
  249. edsl/utilities/repair_functions.py +28 -28
  250. edsl/utilities/restricted_python.py +70 -70
  251. edsl/utilities/utilities.py +424 -436
  252. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/LICENSE +21 -21
  253. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/METADATA +10 -12
  254. edsl-0.1.39.dev3.dist-info/RECORD +277 -0
  255. edsl/agents/QuestionInstructionPromptBuilder.py +0 -128
  256. edsl/agents/QuestionOptionProcessor.py +0 -172
  257. edsl/agents/QuestionTemplateReplacementsBuilder.py +0 -137
  258. edsl/coop/CoopFunctionsMixin.py +0 -15
  259. edsl/coop/ExpectedParrotKeyHandler.py +0 -125
  260. edsl/exceptions/inference_services.py +0 -5
  261. edsl/inference_services/AvailableModelCacheHandler.py +0 -184
  262. edsl/inference_services/AvailableModelFetcher.py +0 -209
  263. edsl/inference_services/ServiceAvailability.py +0 -135
  264. edsl/inference_services/data_structures.py +0 -62
  265. edsl/jobs/AnswerQuestionFunctionConstructor.py +0 -188
  266. edsl/jobs/FetchInvigilator.py +0 -40
  267. edsl/jobs/InterviewTaskManager.py +0 -98
  268. edsl/jobs/InterviewsConstructor.py +0 -48
  269. edsl/jobs/JobsComponentConstructor.py +0 -189
  270. edsl/jobs/JobsRemoteInferenceLogger.py +0 -239
  271. edsl/jobs/RequestTokenEstimator.py +0 -30
  272. edsl/jobs/buckets/TokenBucketAPI.py +0 -211
  273. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  274. edsl/jobs/decorators.py +0 -35
  275. edsl/jobs/jobs_status_enums.py +0 -9
  276. edsl/jobs/loggers/HTMLTableJobLogger.py +0 -304
  277. edsl/language_models/ComputeCost.py +0 -63
  278. edsl/language_models/PriceManager.py +0 -127
  279. edsl/language_models/RawResponseHandler.py +0 -106
  280. edsl/language_models/ServiceDataSources.py +0 -0
  281. edsl/language_models/key_management/KeyLookup.py +0 -63
  282. edsl/language_models/key_management/KeyLookupBuilder.py +0 -273
  283. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  284. edsl/language_models/key_management/__init__.py +0 -0
  285. edsl/language_models/key_management/models.py +0 -131
  286. edsl/notebooks/NotebookToLaTeX.py +0 -142
  287. edsl/questions/ExceptionExplainer.py +0 -77
  288. edsl/questions/HTMLQuestion.py +0 -103
  289. edsl/questions/LoopProcessor.py +0 -149
  290. edsl/questions/QuestionMatrix.py +0 -265
  291. edsl/questions/ResponseValidatorFactory.py +0 -28
  292. edsl/questions/templates/matrix/__init__.py +0 -1
  293. edsl/questions/templates/matrix/answering_instructions.jinja +0 -5
  294. edsl/questions/templates/matrix/question_presentation.jinja +0 -20
  295. edsl/results/MarkdownToDocx.py +0 -122
  296. edsl/results/MarkdownToPDF.py +0 -111
  297. edsl/results/TextEditor.py +0 -50
  298. edsl/results/smart_objects.py +0 -96
  299. edsl/results/table_data_class.py +0 -12
  300. edsl/results/table_renderers.py +0 -118
  301. edsl/scenarios/ConstructDownloadLink.py +0 -109
  302. edsl/scenarios/DirectoryScanner.py +0 -96
  303. edsl/scenarios/DocumentChunker.py +0 -102
  304. edsl/scenarios/DocxScenario.py +0 -16
  305. edsl/scenarios/PdfExtractor.py +0 -40
  306. edsl/scenarios/ScenarioSelector.py +0 -156
  307. edsl/scenarios/file_methods.py +0 -85
  308. edsl/scenarios/handlers/__init__.py +0 -13
  309. edsl/scenarios/handlers/csv.py +0 -38
  310. edsl/scenarios/handlers/docx.py +0 -76
  311. edsl/scenarios/handlers/html.py +0 -37
  312. edsl/scenarios/handlers/json.py +0 -111
  313. edsl/scenarios/handlers/latex.py +0 -5
  314. edsl/scenarios/handlers/md.py +0 -51
  315. edsl/scenarios/handlers/pdf.py +0 -68
  316. edsl/scenarios/handlers/png.py +0 -39
  317. edsl/scenarios/handlers/pptx.py +0 -105
  318. edsl/scenarios/handlers/py.py +0 -294
  319. edsl/scenarios/handlers/sql.py +0 -313
  320. edsl/scenarios/handlers/sqlite.py +0 -149
  321. edsl/scenarios/handlers/txt.py +0 -33
  322. edsl/surveys/ConstructDAG.py +0 -92
  323. edsl/surveys/EditSurvey.py +0 -221
  324. edsl/surveys/InstructionHandler.py +0 -100
  325. edsl/surveys/MemoryManagement.py +0 -72
  326. edsl/surveys/RuleManager.py +0 -172
  327. edsl/surveys/Simulator.py +0 -75
  328. edsl/surveys/SurveyToApp.py +0 -141
  329. edsl/utilities/PrettyList.py +0 -56
  330. edsl/utilities/is_notebook.py +0 -18
  331. edsl/utilities/is_valid_variable_name.py +0 -11
  332. edsl/utilities/remove_edsl_version.py +0 -24
  333. edsl-0.1.39.dev2.dist-info/RECORD +0 -352
  334. {edsl-0.1.39.dev2.dist-info → edsl-0.1.39.dev3.dist-info}/WHEEL +0 -0
@@ -1,273 +0,0 @@
1
- from typing import Optional, List
2
- from collections import UserDict
3
- import os
4
- from functools import lru_cache
5
- from dataclasses import dataclass, asdict
6
-
7
- from edsl.enums import service_to_api_keyname
8
- from edsl.exceptions.general import MissingAPIKeyError
9
-
10
- from edsl.language_models.key_management.KeyLookup import KeyLookup
11
-
12
- from edsl.language_models.key_management.models import (
13
- APIKeyEntry,
14
- LimitEntry,
15
- APIIDEntry,
16
- LanguageModelInput,
17
- )
18
-
19
- service_to_api_keyname["bedrock"] = "AWS_SECRET_ACCESS_KEY"
20
- service_to_api_id = {"bedrock": "AWS_ACCESS_KEY_ID"}
21
-
22
- api_keyname_to_service = {}
23
-
24
- for service, key in service_to_api_keyname.items():
25
- if isinstance(key, list):
26
- for k in key:
27
- api_keyname_to_service[k] = service
28
- else:
29
- api_keyname_to_service[key] = service
30
-
31
- api_id_to_service = {"AWS_ACCESS_KEY_ID": "bedrock"}
32
-
33
-
34
- class KeyLookupBuilder:
35
- """Builds KeyLookup options.
36
-
37
- >>> builder = KeyLookupBuilder(fetch_order=("config", "env"))
38
- >>> builder.DEFAULT_RPM
39
- 10
40
- >>> builder.DEFAULT_TPM
41
- 2000000
42
- >>> builder.fetch_order
43
- ('config', 'env')
44
-
45
- Test invalid fetch_order:
46
- >>> try:
47
- ... KeyLookupBuilder(fetch_order=["config", "env"]) # Should be tuple
48
- ... except ValueError as e:
49
- ... str(e)
50
- 'fetch_order must be a tuple'
51
-
52
- Test service extraction:
53
- >>> builder.extract_service("EDSL_SERVICE_RPM_OPENAI")
54
- ('openai', 'rpm')
55
- """
56
-
57
- DEFAULT_RPM = 10
58
- DEFAULT_TPM = 2000000
59
-
60
- def __init__(self, fetch_order: Optional[tuple[str]] = None):
61
- if fetch_order is None:
62
- self.fetch_order = ("config", "env")
63
- else:
64
- self.fetch_order = fetch_order
65
-
66
- if not isinstance(self.fetch_order, tuple):
67
- raise ValueError("fetch_order must be a tuple")
68
-
69
- self.limit_data = {}
70
- self.key_data = {}
71
- self.id_data = {}
72
- self.process_key_value_pairs()
73
-
74
- @property
75
- def known_services(self):
76
- """Get the set of known services.
77
-
78
- >>> builder = KeyLookupBuilder()
79
- >>> isinstance(builder.known_services, set)
80
- True
81
- """
82
- return set(self.key_data.keys()) | set(self.limit_data.keys())
83
-
84
- @lru_cache
85
- def build(self) -> "KeyLookup":
86
- """Build a KeyLookup instance.
87
-
88
- >>> builder = KeyLookupBuilder()
89
- >>> lookup = builder.build()
90
- >>> isinstance(lookup, KeyLookup)
91
- True
92
- >>> lookup['test'].api_token # Test service should always exist
93
- 'test'
94
- """
95
- d = {}
96
- for service in self.known_services:
97
- try:
98
- d[service] = self.get_language_model_input(service)
99
- except MissingAPIKeyError:
100
- pass
101
-
102
- d.update({"test": LanguageModelInput(api_token="test", rpm=10, tpm=2000000)})
103
- return KeyLookup(d)
104
-
105
- def get_language_model_input(self, service: str) -> LanguageModelInput:
106
- """Get the language model input for a given service.
107
-
108
- >>> builder = KeyLookupBuilder()
109
- >>> try:
110
- ... builder.get_language_model_input("nonexistent_service")
111
- ... except MissingAPIKeyError as e:
112
- ... str(e)
113
- "No key found for service 'nonexistent_service'"
114
- """
115
- if (key_entries := self.key_data.get(service)) is None:
116
- raise MissingAPIKeyError(f"No key found for service '{service}'")
117
-
118
- if len(key_entries) == 1:
119
- api_key_entry = key_entries[0]
120
-
121
- id_entry = self.id_data.get(service)
122
- id_source = id_entry.source if id_entry is not None else None
123
- api_id = id_entry.value if id_entry is not None else None
124
-
125
- if (limit_entry := self.limit_data.get(service)) is None:
126
- limit_entry = LimitEntry(
127
- service=service,
128
- rpm=self.DEFAULT_RPM,
129
- tpm=self.DEFAULT_TPM,
130
- source="default",
131
- )
132
-
133
- if limit_entry.rpm is None:
134
- limit_entry.rpm = self.DEFAULT_RPM
135
- if limit_entry.tpm is None:
136
- limit_entry.tpm = self.DEFAULT_TPM
137
-
138
- return LanguageModelInput(
139
- api_token=api_key_entry.value,
140
- rpm=int(limit_entry.rpm),
141
- tpm=int(limit_entry.tpm),
142
- api_id=api_id,
143
- token_source=api_key_entry.source,
144
- limit_source=limit_entry.source,
145
- id_source=id_source,
146
- )
147
-
148
- def __repr__(self):
149
- return f"DataSource(key_data={self.key_data}, limit_data={self.limit_data}, id_data={self.id_data})"
150
-
151
- def _os_env_key_value_pairs(self):
152
- return dict(list(os.environ.items()))
153
-
154
- def _coop_key_value_pairs(self):
155
- from edsl.coop import Coop
156
-
157
- c = Coop()
158
- return dict(list(c.fetch_rate_limit_config_vars().items()))
159
-
160
- def _config_key_value_pairs(self):
161
- from edsl.config import CONFIG
162
-
163
- return dict(list(CONFIG.items()))
164
-
165
- @staticmethod
166
- def extract_service(key: str) -> str:
167
- """Extract the service and limit type from the key"""
168
- limit_type, service_raw = key.replace("EDSL_SERVICE_", "").split("_")
169
- return service_raw.lower(), limit_type.lower()
170
-
171
- def get_key_value_pairs(self) -> dict:
172
- """Get key-value pairs from configured sources."""
173
- fetching_functions = {
174
- "env": self._os_env_key_value_pairs,
175
- "coop": self._coop_key_value_pairs,
176
- "config": self._config_key_value_pairs,
177
- }
178
- d = {}
179
- for source in self.fetch_order:
180
- f = fetching_functions[source]
181
- new_data = f()
182
- for k, v in new_data.items():
183
- d[k] = (v, source)
184
- return d
185
-
186
- def _entry_type(self, key, value) -> str:
187
- """Determine the type of entry from a key.
188
-
189
- >>> builder = KeyLookupBuilder()
190
- >>> builder._entry_type("EDSL_SERVICE_RPM_OPENAI", "60")
191
- 'limit'
192
- >>> builder._entry_type("OPENAI_API_KEY", "sk-1234")
193
- 'api_key'
194
- >>> builder._entry_type("AWS_ACCESS_KEY_ID", "AKIA1234")
195
- 'api_id'
196
- >>> builder._entry_type("UNKNOWN_KEY", "value")
197
- 'unknown'
198
- """
199
- if key.startswith("EDSL_SERVICE_"):
200
- return "limit"
201
- elif key in api_keyname_to_service:
202
- return "api_key"
203
- elif key in api_id_to_service:
204
- return "api_id"
205
- return "unknown"
206
-
207
- def _add_id(self, key: str, value: str, source: str) -> None:
208
- """Add an API ID to the id_data dictionary.
209
-
210
- >>> builder = KeyLookupBuilder()
211
- >>> builder._add_id("AWS_ACCESS_KEY_ID", "AKIA1234", "env")
212
- >>> builder.id_data["bedrock"].value
213
- 'AKIA1234'
214
- >>> try:
215
- ... builder._add_id("AWS_ACCESS_KEY_ID", "AKIA5678", "env")
216
- ... except ValueError as e:
217
- ... str(e)
218
- 'Duplicate ID for service bedrock'
219
- """
220
- service = api_id_to_service[key]
221
- if service not in self.id_data:
222
- self.id_data[service] = APIIDEntry(
223
- service=service, name=key, value=value, source=source
224
- )
225
- else:
226
- raise ValueError(f"Duplicate ID for service {service}")
227
-
228
- def _add_limit(self, key: str, value: str, source: str) -> None:
229
- """Add a rate limit entry to the limit_data dictionary.
230
-
231
- >>> builder = KeyLookupBuilder()
232
- >>> builder._add_limit("EDSL_SERVICE_RPM_OPENAI", "60", "config")
233
- >>> builder.limit_data["openai"].rpm
234
- '60'
235
- >>> builder._add_limit("EDSL_SERVICE_TPM_OPENAI", "100000", "config")
236
- >>> builder.limit_data["openai"].tpm
237
- '100000'
238
- """
239
- service, limit_type = self.extract_service(key)
240
- if service in self.limit_data:
241
- setattr(self.limit_data[service], limit_type.lower(), value)
242
- else:
243
- new_limit_entry = LimitEntry(
244
- service=service, rpm=None, tpm=None, source=source
245
- )
246
- setattr(new_limit_entry, limit_type.lower(), value)
247
- self.limit_data[service] = new_limit_entry
248
-
249
- def _add_api_key(self, key: str, value: str, source: str) -> None:
250
- """Add an API key entry to the key_data dictionary.
251
-
252
- >>> builder = KeyLookupBuilder()
253
- >>> builder._add_api_key("OPENAI_API_KEY", "sk-1234", "env")
254
- >>> 'sk-1234' == builder.key_data["openai"][-1].value
255
- True
256
- """
257
- service = api_keyname_to_service[key]
258
- new_entry = APIKeyEntry(service=service, name=key, value=value, source=source)
259
- if service not in self.key_data:
260
- self.key_data[service] = [new_entry]
261
- else:
262
- self.key_data[service].append(new_entry)
263
-
264
- def process_key_value_pairs(self) -> None:
265
- """Process all key-value pairs from the configured sources."""
266
- for key, value_pair in self.get_key_value_pairs().items():
267
- value, source = value_pair
268
- if (entry_type := self._entry_type(key, value)) == "limit":
269
- self._add_limit(key, value, source)
270
- elif entry_type == "api_key":
271
- self._add_api_key(key, value, source)
272
- elif entry_type == "api_id":
273
- self._add_id(key, value, source)
@@ -1,38 +0,0 @@
1
- from collections import UserDict
2
-
3
- from edsl.language_models.key_management.KeyLookupBuilder import KeyLookupBuilder
4
-
5
-
6
- class KeyLookupCollection(UserDict):
7
- """A singleton class that stores key-lookup objects.
8
-
9
- This is because once a KeyLook is created once, we do not
10
- need to keep re-creating it.
11
-
12
- >>> collection = KeyLookupCollection()
13
- >>> collection2 = KeyLookupCollection()
14
- >>> collection is collection2 # Test singleton pattern
15
- True
16
- >>> collection.add_key_lookup(("config", "env"))
17
- >>> ("config", "env") in collection.data
18
- True
19
- """
20
-
21
- _instance = None
22
-
23
- def __new__(cls, *args, **kwargs):
24
- if cls._instance is None:
25
- cls._instance = super().__new__(cls)
26
- return cls._instance
27
-
28
- def __init__(self, *args, **kwargs):
29
- if not hasattr(self, "_initialized"):
30
- self.data = {}
31
- self._initialized = True
32
- super().__init__(*args, **kwargs)
33
-
34
- def add_key_lookup(self, fetch_order=None):
35
- if fetch_order is None:
36
- fetch_order = ("config", "env")
37
- if fetch_order not in self.data:
38
- self.data[fetch_order] = KeyLookupBuilder(fetch_order=fetch_order).build()
File without changes
@@ -1,131 +0,0 @@
1
- from dataclasses import dataclass, asdict
2
- from typing import Optional
3
-
4
-
5
- @dataclass
6
- class APIKeyEntry:
7
- """A class representing an API key entry.
8
-
9
- >>> entry = APIKeyEntry.example()
10
- >>> entry.service
11
- 'openai'
12
- >>> entry.name
13
- 'OPENAI_API_KEY'
14
- >>> entry.value
15
- 'sk-abcd1234'
16
- >>> entry.source
17
- 'env'
18
- """
19
-
20
- service: str
21
- name: str
22
- value: str
23
- source: Optional[str] = None
24
-
25
- @classmethod
26
- def example(cls):
27
- return APIKeyEntry(
28
- service="openai", name="OPENAI_API_KEY", value="sk-abcd1234", source="env"
29
- )
30
-
31
-
32
- @dataclass
33
- class LimitEntry:
34
- """A class representing rate limit entries for a service.
35
-
36
- >>> limit = LimitEntry.example()
37
- >>> limit.service
38
- 'openai'
39
- >>> limit.rpm
40
- 60
41
- >>> limit.tpm
42
- 100000
43
- >>> limit.source
44
- 'config'
45
- """
46
-
47
- service: str
48
- rpm: int
49
- tpm: int
50
- source: Optional[str] = None
51
-
52
- @classmethod
53
- def example(cls):
54
- return LimitEntry(service="openai", rpm=60, tpm=100000, source="config")
55
-
56
-
57
- @dataclass
58
- class APIIDEntry:
59
- """A class representing an API ID entry.
60
-
61
- >>> id_entry = APIIDEntry.example()
62
- >>> id_entry.service
63
- 'bedrock'
64
- >>> id_entry.name
65
- 'AWS_ACCESS_KEY_ID'
66
- >>> id_entry.value
67
- 'AKIA1234'
68
- >>> id_entry.source
69
- 'env'
70
- """
71
-
72
- service: str
73
- name: str
74
- value: str
75
- source: Optional[str] = None
76
-
77
- @classmethod
78
- def example(cls):
79
- return APIIDEntry(
80
- service="bedrock", name="AWS_ACCESS_KEY_ID", value="AKIA1234", source="env"
81
- )
82
-
83
-
84
- @dataclass
85
- class LanguageModelInput:
86
- """A class representing input configuration for a language model service.
87
-
88
- >>> lm_input = LanguageModelInput.example()
89
- >>> lm_input.api_token
90
- 'sk-abcd123'
91
- >>> lm_input.rpm
92
- 60
93
- >>> lm_input.tpm
94
- 100000
95
- >>> lm_input.api_id
96
-
97
-
98
- Test dictionary conversion:
99
- >>> d = lm_input.to_dict()
100
- >>> isinstance(d, dict)
101
- True
102
- >>> LanguageModelInput.from_dict(d).api_token == lm_input.api_token
103
- True
104
- """
105
-
106
- api_token: str
107
- rpm: int
108
- tpm: int
109
- api_id: Optional[str] = None
110
- token_source: Optional[str] = None
111
- limit_source: Optional[str] = None
112
- id_source: Optional[str] = None
113
-
114
- def to_dict(self):
115
- return asdict(self)
116
-
117
- @classmethod
118
- def from_dict(cls, d):
119
- return cls(**d)
120
-
121
- @classmethod
122
- def example(cls):
123
- return LanguageModelInput(
124
- api_token="sk-abcd123", tpm=100000, rpm=60, api_id=None
125
- )
126
-
127
-
128
- if __name__ == "__main__":
129
- import doctest
130
-
131
- doctest.testmod()
@@ -1,142 +0,0 @@
1
- from typing import Optional, Dict
2
- import os
3
- import nbformat
4
- from nbconvert.exporters import LatexExporter
5
- from nbconvert.writers import FilesWriter
6
-
7
-
8
- class NotebookToLaTeX:
9
- """
10
- A class for converting Jupyter notebooks to LaTeX with proper directory structure.
11
- """
12
-
13
- def __init__(self, notebook):
14
- """
15
- Initialize with a Notebook instance.
16
-
17
- :param notebook: An instance of the Notebook class
18
- """
19
- self.notebook = notebook
20
- self.latex_exporter = LatexExporter()
21
- self._configure_exporter()
22
-
23
- def _configure_exporter(self):
24
- """Configure the LaTeX exporter with default settings."""
25
- self.latex_exporter.exclude_input_prompt = True
26
- self.latex_exporter.exclude_output_prompt = True
27
- self.latex_exporter.template_name = "classic"
28
-
29
- def _create_makefile(self, filename: str, output_dir: str):
30
- """Create a Makefile for the LaTeX project."""
31
- makefile_content = f"""# Makefile for {filename}
32
- all: pdf
33
-
34
- pdf: {filename}.pdf
35
-
36
- {filename}.pdf: {filename}.tex
37
- \tpdflatex {filename}.tex
38
- \tpdflatex {filename}.tex # Run twice for references
39
- \tbibtex {filename} # Run bibtex if needed
40
- \tpdflatex {filename}.tex # Run one more time for bibtex
41
-
42
- clean:
43
- \trm -f *.aux *.log *.out *.toc *.pdf *.bbl *.blg
44
- """
45
- makefile_path = os.path.join(output_dir, "Makefile")
46
- with open(makefile_path, "w") as f:
47
- f.write(makefile_content)
48
-
49
- def _create_readme(self, filename: str, output_dir: str):
50
- """Create a README file with usage instructions."""
51
- readme_content = f"""# {filename}
52
-
53
- This folder contains the LaTeX version of your Jupyter notebook.
54
-
55
- Files:
56
- - {filename}.tex: Main LaTeX file
57
- - Makefile: Build automation
58
-
59
- To compile the PDF:
60
- 1. Make sure you have a LaTeX distribution installed (e.g., TexLive)
61
- 2. Run `make` in this directory
62
- 3. The output will be {filename}.pdf
63
-
64
- To clean up build files:
65
- - Run `make clean`
66
- """
67
- readme_path = os.path.join(output_dir, "README.md")
68
- with open(readme_path, "w") as f:
69
- f.write(readme_content)
70
-
71
- def convert(self, filename: str, output_dir: Optional[str] = None):
72
- """
73
- Convert the notebook to LaTeX and create a project directory.
74
-
75
- :param filename: Name for the output files (without extension)
76
- :param output_dir: Optional directory path. If None, uses filename as directory
77
- """
78
- # Use filename as directory if no output_dir specified
79
- output_dir = output_dir or filename
80
-
81
- # Create output directory
82
- os.makedirs(output_dir, exist_ok=True)
83
-
84
- # Convert notebook to nbformat
85
- notebook_node = nbformat.from_dict(self.notebook.data)
86
-
87
- # Convert to LaTeX
88
- body, resources = self.latex_exporter.from_notebook_node(notebook_node)
89
-
90
- # Write the main tex file
91
- output_file_path = os.path.join(output_dir, f"{filename}.tex")
92
- with open(output_file_path, "w", encoding="utf-8") as f:
93
- f.write(body)
94
-
95
- # Write additional resources (images, etc.)
96
- if resources.get("outputs"):
97
- for fname, data in resources["outputs"].items():
98
- resource_path = os.path.join(output_dir, fname)
99
- with open(resource_path, "wb") as f:
100
- f.write(data)
101
-
102
- # Create supporting files
103
- self._create_makefile(filename, output_dir)
104
- self._create_readme(filename, output_dir)
105
-
106
- def set_template(self, template_name: str):
107
- """
108
- Set the LaTeX template to use.
109
-
110
- :param template_name: Name of the template (e.g., 'classic', 'article')
111
- """
112
- self.latex_exporter.template_name = template_name
113
-
114
- def set_template_options(self, options: Dict):
115
- """
116
- Set additional template options.
117
-
118
- :param options: Dictionary of template options
119
- """
120
- for key, value in options.items():
121
- setattr(self.latex_exporter, key, value)
122
-
123
-
124
- # Example usage:
125
- if __name__ == "__main__":
126
- from edsl import Notebook
127
-
128
- # Create or load a notebook
129
- notebook = Notebook.example()
130
-
131
- # Create converter and convert
132
- converter = NotebookToLaTeX(notebook)
133
- converter.convert("example_output")
134
-
135
- # Example with custom template options
136
- converter.set_template_options(
137
- {
138
- "exclude_input": True, # Hide input cells
139
- "exclude_output": False, # Show output cells
140
- }
141
- )
142
- converter.convert("example_output_custom")
@@ -1,77 +0,0 @@
1
- from pydantic import ValidationError
2
- from typing import Union
3
-
4
-
5
- class ExceptionExplainer:
6
- """
7
- A class that converts validation errors into human-readable explanations,
8
- specifically for Language Model responses.
9
- """
10
-
11
- def __init__(self, error: Union[ValidationError, Exception], model_response: str):
12
- """
13
- Initialize the explainer with the error and model response.
14
-
15
- Args:
16
- error: The validation error that occurred
17
- model_response: The raw response from the Language Model
18
- """
19
- self.error = error
20
- self.model_response = model_response
21
-
22
- def explain(self) -> str:
23
- """
24
- Generate a human-readable explanation of why the model's response failed validation.
25
-
26
- Returns:
27
- A user-friendly explanation of why the model's response was invalid
28
- """
29
- self.error = self.error.pydantic_error
30
- return self._explain_validation_error()
31
-
32
- # Fallback for unknown errors
33
- return self._create_generic_explanation()
34
-
35
- def _explain_validation_error(self) -> str:
36
- """Handle Pydantic ValidationError specifically."""
37
- error_dict = self.error.errors()
38
- explanations = []
39
-
40
- context = f'The AI model returned "{self.model_response}", but this was invalid for the question you asked and the constraints you provided.\n'
41
- explanations.append(context)
42
- explanations.append("Reason(s) invalidated:")
43
- for e in error_dict:
44
- msg = e.get("msg", "Unknown error")
45
- explanations.append(f"- {msg}")
46
-
47
- main_message = "\n".join(explanations)
48
- return f"{main_message}\n\n{self._get_suggestion()}"
49
-
50
- def _create_generic_explanation(self) -> str:
51
- """Create a generic explanation for non-ValidationError exceptions."""
52
- return (
53
- f'The AI model returned "{self.model_response}", but this response was invalid. '
54
- f"Error: {str(self.error)}"
55
- )
56
-
57
- def _get_suggestion(self) -> str:
58
- """Get a suggestion for handling the error."""
59
- return (
60
- "EDSL Advice:\n"
61
- "- Look at the Model comments - often the model will provide a hint about what went wrong.\n"
62
- "- If the model's response doesn't make sense, try rephrasing your question.\n"
63
- "- Try using 'use_code' parameter of a MultipleChoice.\n"
64
- "- A QuestionFreeText will almost always validate.\n"
65
- "- Try setting the 'permissive' = True parameter in the Question constructor."
66
- )
67
-
68
-
69
- # Example usage:
70
- if __name__ == "__main__":
71
- try:
72
- # Your validation code here
73
- raise ValidationError.parse_obj({"answer": "120"})
74
- except ValidationError as e:
75
- explainer = ExceptionExplainer(e, "120")
76
- explanation = explainer.explain()
77
- print(explanation)