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,304 +0,0 @@
1
- import re
2
- import uuid
3
- from datetime import datetime
4
- from IPython.display import display, HTML
5
- from edsl.jobs.JobsRemoteInferenceLogger import JobLogger
6
- from edsl.jobs.jobs_status_enums import JobsStatus
7
-
8
-
9
- class HTMLTableJobLogger(JobLogger):
10
- def __init__(self, verbose=True, theme="auto", **kwargs):
11
- super().__init__(verbose=verbose)
12
- self.display_handle = display(HTML(""), display_id=True)
13
- self.current_message = None
14
- self.log_id = str(uuid.uuid4())
15
- self.is_expanded = True
16
- self.spinner_chars = ["◐", "◓", "◑", "◒"]
17
- self.spinner_idx = 0
18
- self.theme = theme # Can be "auto", "light", or "dark"
19
-
20
- # Initialize CSS once when the logger is created
21
- self._init_css()
22
-
23
- def _init_css(self):
24
- """Initialize the CSS styles with enhanced theme support"""
25
- css = """
26
- <style>
27
- /* Base theme variables */
28
- :root {
29
- --jl-bg-primary: #ffffff;
30
- --jl-bg-secondary: #f5f5f5;
31
- --jl-border-color: #e0e0e0;
32
- --jl-text-primary: #24292e;
33
- --jl-text-secondary: #586069;
34
- --jl-link-color: #0366d6;
35
- --jl-success-color: #28a745;
36
- --jl-error-color: #d73a49;
37
- --jl-header-bg: #f1f1f1;
38
- }
39
-
40
- /* Dark theme variables */
41
- .theme-dark {
42
- --jl-bg-primary: #1e1e1e;
43
- --jl-bg-secondary: #252526;
44
- --jl-border-color: #2d2d2d;
45
- --jl-text-primary: #cccccc;
46
- --jl-text-secondary: #999999;
47
- --jl-link-color: #4e94ce;
48
- --jl-success-color: #89d185;
49
- --jl-error-color: #f14c4c;
50
- --jl-header-bg: #333333;
51
- }
52
-
53
- /* High contrast theme variables */
54
- .theme-high-contrast {
55
- --jl-bg-primary: #000000;
56
- --jl-bg-secondary: #1a1a1a;
57
- --jl-border-color: #404040;
58
- --jl-text-primary: #ffffff;
59
- --jl-text-secondary: #cccccc;
60
- --jl-link-color: #66b3ff;
61
- --jl-success-color: #00ff00;
62
- --jl-error-color: #ff0000;
63
- --jl-header-bg: #262626;
64
- }
65
-
66
- .job-logger {
67
- font-family: system-ui, -apple-system, sans-serif;
68
- max-width: 800px;
69
- margin: 10px 0;
70
- color: var(--jl-text-primary);
71
- box-shadow: 0 1px 3px rgba(0,0,0,0.12);
72
- border-radius: 4px;
73
- overflow: hidden;
74
- }
75
-
76
- .job-logger-header {
77
- padding: 12px 16px;
78
- background: var(--jl-header-bg);
79
- border: none;
80
- border-radius: 4px 4px 0 0;
81
- cursor: pointer;
82
- color: var(--jl-text-primary);
83
- user-select: none;
84
- font-weight: 500;
85
- letter-spacing: 0.3px;
86
- display: flex;
87
- justify-content: space-between;
88
- align-items: center;
89
- }
90
-
91
- .theme-select {
92
- padding: 4px 8px;
93
- border-radius: 4px;
94
- border: 1px solid var(--jl-border-color);
95
- background: var(--jl-bg-primary);
96
- color: var(--jl-text-primary);
97
- font-size: 0.9em;
98
- }
99
-
100
- .job-logger-table {
101
- width: 100%;
102
- border-collapse: separate;
103
- border-spacing: 0;
104
- background: var(--jl-bg-primary);
105
- border: 1px solid var(--jl-border-color);
106
- margin-top: -1px;
107
- }
108
-
109
- .job-logger-cell {
110
- padding: 12px 16px;
111
- border-bottom: 1px solid var(--jl-border-color);
112
- line-height: 1.4;
113
- }
114
-
115
- .job-logger-label {
116
- font-weight: 500;
117
- color: var(--jl-text-primary);
118
- width: 25%;
119
- background: var(--jl-bg-secondary);
120
- }
121
-
122
- .job-logger-value {
123
- color: var(--jl-text-secondary);
124
- word-break: break-word;
125
- }
126
-
127
- .job-logger-status {
128
- margin: 0;
129
- padding: 12px 16px;
130
- background-color: var(--jl-bg-secondary);
131
- border: 1px solid var(--jl-border-color);
132
- border-top: none;
133
- border-radius: 0 0 4px 4px;
134
- color: var(--jl-text-primary);
135
- font-size: 0.95em;
136
- }
137
- </style>
138
-
139
- <script>
140
- class ThemeManager {
141
- constructor(logId, initialTheme = 'auto') {
142
- this.logId = logId;
143
- this.currentTheme = initialTheme;
144
- this.darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
145
- this.init();
146
- }
147
-
148
- init() {
149
- this.setupThemeSwitcher();
150
- this.updateTheme(this.currentTheme);
151
-
152
- this.darkModeMediaQuery.addListener(() => {
153
- if (this.currentTheme === 'auto') {
154
- this.updateTheme('auto');
155
- }
156
- });
157
- }
158
-
159
- setupThemeSwitcher() {
160
- const logger = document.querySelector(`#logger-${this.logId}`);
161
- if (!logger) return;
162
-
163
- const switcher = document.createElement('div');
164
- switcher.className = 'theme-switcher';
165
- switcher.innerHTML = `
166
- <select id="theme-select-${this.logId}" class="theme-select">
167
- <option value="auto">Auto</option>
168
- <option value="light">Light</option>
169
- <option value="dark">Dark</option>
170
- <option value="high-contrast">High Contrast</option>
171
- </select>
172
- `;
173
-
174
- const header = logger.querySelector('.job-logger-header');
175
- header.appendChild(switcher);
176
-
177
- const select = switcher.querySelector('select');
178
- select.value = this.currentTheme;
179
- select.addEventListener('change', (e) => {
180
- this.updateTheme(e.target.value);
181
- });
182
- }
183
-
184
- updateTheme(theme) {
185
- const logger = document.querySelector(`#logger-${this.logId}`);
186
- if (!logger) return;
187
-
188
- this.currentTheme = theme;
189
-
190
- logger.classList.remove('theme-light', 'theme-dark', 'theme-high-contrast');
191
-
192
- if (theme === 'auto') {
193
- const isDark = this.darkModeMediaQuery.matches;
194
- logger.classList.add(isDark ? 'theme-dark' : 'theme-light');
195
- } else {
196
- logger.classList.add(`theme-${theme}`);
197
- }
198
-
199
- try {
200
- localStorage.setItem('jobLoggerTheme', theme);
201
- } catch (e) {
202
- console.warn('Unable to save theme preference:', e);
203
- }
204
- }
205
- }
206
-
207
- window.initThemeManager = (logId, initialTheme) => {
208
- new ThemeManager(logId, initialTheme);
209
- };
210
- </script>
211
- """
212
-
213
- init_script = f"""
214
- <script>
215
- document.addEventListener('DOMContentLoaded', () => {{
216
- window.initThemeManager('{self.log_id}', '{self.theme}');
217
- }});
218
- </script>
219
- """
220
-
221
- display(HTML(css + init_script))
222
-
223
- def _get_table_row(self, key: str, value: str) -> str:
224
- """Generate a table row with key-value pair"""
225
- return f"""
226
- <tr>
227
- <td class="job-logger-cell job-logger-label">{key}</td>
228
- <td class="job-logger-cell job-logger-value">{value if value else 'None'}</td>
229
- </tr>
230
- """
231
-
232
- def _linkify(self, text: str) -> str:
233
- """Convert URLs in text to clickable links"""
234
- url_pattern = r'(https?://[^\s<>"]+|www\.[^\s<>"]+)'
235
- return re.sub(
236
- url_pattern,
237
- r'<a href="\1" target="_blank" class="job-logger-link">\1</a>',
238
- text,
239
- )
240
-
241
- def _get_spinner(self, status: JobsStatus) -> str:
242
- """Get the current spinner frame if status is running"""
243
- if status == JobsStatus.RUNNING:
244
- spinner = self.spinner_chars[self.spinner_idx]
245
- self.spinner_idx = (self.spinner_idx + 1) % len(self.spinner_chars)
246
- return f'<span style="margin-right: 8px;">{spinner}</span>'
247
- elif status == JobsStatus.COMPLETED:
248
- return (
249
- '<span style="margin-right: 8px;" class="job-logger-success">✓</span>'
250
- )
251
- elif status == JobsStatus.FAILED:
252
- return '<span style="margin-right: 8px;" class="job-logger-error">✗</span>'
253
- return ""
254
-
255
- def _get_html(self, status: JobsStatus = JobsStatus.RUNNING) -> str:
256
- """Generate the complete HTML display with theme support"""
257
- info_rows = ""
258
- for field, _ in self.jobs_info.__annotations__.items():
259
- if field != "pretty_names":
260
- value = getattr(self.jobs_info, field)
261
- value = self._linkify(str(value)) if value else None
262
- pretty_name = self.jobs_info.pretty_names.get(
263
- field, field.replace("_", " ").title()
264
- )
265
- info_rows += self._get_table_row(pretty_name, value)
266
-
267
- message_html = ""
268
- if self.current_message:
269
- spinner = self._get_spinner(status)
270
- message_html = f"""
271
- <div class="job-logger-status">
272
- {spinner}<strong>Current Status:</strong> {self._linkify(self.current_message)}
273
- </div>
274
- """
275
-
276
- display_style = "block" if self.is_expanded else "none"
277
- arrow = "▼" if self.is_expanded else "▶"
278
-
279
- return f"""
280
- <!-- #region Remove Inference Info -->
281
- <div id="logger-{self.log_id}" class="job-logger">
282
- <div class="job-logger-header">
283
- <span>
284
- <span id="arrow-{self.log_id}">{arrow}</span>
285
- Job Status ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
286
- </span>
287
- </div>
288
- <div id="content-{self.log_id}" style="display: {display_style};">
289
- <table class="job-logger-table">
290
- {info_rows}
291
- </table>
292
- {message_html}
293
- </div>
294
- </div>
295
- <!-- # endregion -->
296
- """
297
-
298
- def update(self, message: str, status: JobsStatus = JobsStatus.RUNNING):
299
- """Update the display with new message and current JobsInfo state"""
300
- self.current_message = message
301
- if self.verbose:
302
- self.display_handle.update(HTML(self._get_html(status)))
303
- else:
304
- return None
@@ -1,63 +0,0 @@
1
- from typing import Any, Union
2
-
3
-
4
- class ComputeCost:
5
- def __init__(self, language_model: "LanguageModel"):
6
- self.language_model = language_model
7
- self._price_lookup = None
8
-
9
- @property
10
- def price_lookup(self):
11
- if self._price_lookup is None:
12
- from edsl.coop import Coop
13
-
14
- c = Coop()
15
- self._price_lookup = c.fetch_prices()
16
- return self._price_lookup
17
-
18
- def cost(self, raw_response: dict[str, Any]) -> Union[float, str]:
19
- """Return the dollar cost of a raw response."""
20
-
21
- usage = self.get_usage_dict(raw_response)
22
- from edsl.coop import Coop
23
-
24
- c = Coop()
25
- price_lookup = c.fetch_prices()
26
- key = (self._inference_service_, self.model)
27
- if key not in price_lookup:
28
- return f"Could not find price for model {self.model} in the price lookup."
29
-
30
- relevant_prices = price_lookup[key]
31
- try:
32
- input_tokens = int(usage[self.input_token_name])
33
- output_tokens = int(usage[self.output_token_name])
34
- except Exception as e:
35
- return f"Could not fetch tokens from model response: {e}"
36
-
37
- try:
38
- inverse_output_price = relevant_prices["output"]["one_usd_buys"]
39
- inverse_input_price = relevant_prices["input"]["one_usd_buys"]
40
- except Exception as e:
41
- if "output" not in relevant_prices:
42
- return f"Could not fetch prices from {relevant_prices} - {e}; Missing 'output' key."
43
- if "input" not in relevant_prices:
44
- return f"Could not fetch prices from {relevant_prices} - {e}; Missing 'input' key."
45
- return f"Could not fetch prices from {relevant_prices} - {e}"
46
-
47
- if inverse_input_price == "infinity":
48
- input_cost = 0
49
- else:
50
- try:
51
- input_cost = input_tokens / float(inverse_input_price)
52
- except Exception as e:
53
- return f"Could not compute input price - {e}."
54
-
55
- if inverse_output_price == "infinity":
56
- output_cost = 0
57
- else:
58
- try:
59
- output_cost = output_tokens / float(inverse_output_price)
60
- except Exception as e:
61
- return f"Could not compute output price - {e}"
62
-
63
- return input_cost + output_cost
@@ -1,127 +0,0 @@
1
- from typing import Dict, Tuple, Optional, Union
2
-
3
-
4
- class PriceManager:
5
- _instance = None
6
- _price_lookup: Dict[Tuple[str, str], Dict] = {}
7
- _is_initialized = False
8
-
9
- def __new__(cls):
10
- if cls._instance is None:
11
- cls._instance = super(PriceManager, cls).__new__(cls)
12
- return cls._instance
13
-
14
- def __init__(self):
15
- # Only initialize once, even if __init__ is called multiple times
16
- if not self._is_initialized:
17
- self._is_initialized = True
18
- self.refresh_prices()
19
-
20
- def refresh_prices(self) -> None:
21
- """
22
- Fetch fresh prices from the Coop service and update the internal price lookup.
23
-
24
- """
25
- from edsl.coop import Coop
26
-
27
- c = Coop()
28
- try:
29
- self._price_lookup = c.fetch_prices()
30
- except Exception as e:
31
- print(f"Error fetching prices: {str(e)}")
32
-
33
- def get_price(self, inference_service: str, model: str) -> Optional[Dict]:
34
- """
35
- Get the price information for a specific service and model combination.
36
-
37
- Args:
38
- inference_service (str): The name of the inference service
39
- model (str): The model identifier
40
-
41
- Returns:
42
- Optional[Dict]: Price information if found, None otherwise
43
- """
44
- key = (inference_service, model)
45
- return self._price_lookup.get(key)
46
-
47
- def get_all_prices(self) -> Dict[Tuple[str, str], Dict]:
48
- """
49
- Get the complete price lookup dictionary.
50
-
51
- Returns:
52
- Dict[Tuple[str, str], Dict]: The complete price lookup dictionary
53
- """
54
- return self._price_lookup.copy()
55
-
56
- def calculate_cost(
57
- self,
58
- inference_service: str,
59
- model: str,
60
- usage: Dict[str, Union[str, int]],
61
- input_token_name: str,
62
- output_token_name: str,
63
- ) -> Union[float, str]:
64
- """
65
- Calculate the total cost for a model usage based on input and output tokens.
66
-
67
- Args:
68
- inference_service (str): The inference service identifier
69
- model (str): The model identifier
70
- usage (Dict[str, Union[str, int]]): Dictionary containing token usage information
71
- input_token_name (str): Key name for input tokens in the usage dict
72
- output_token_name (str): Key name for output tokens in the usage dict
73
-
74
- Returns:
75
- Union[float, str]: Total cost if calculation successful, error message string if not
76
- """
77
- relevant_prices = self.get_price(inference_service, model)
78
- if relevant_prices is None:
79
- return f"Could not find price for model {model} in the price lookup."
80
-
81
- # Extract token counts
82
- try:
83
- input_tokens = int(usage[input_token_name])
84
- output_tokens = int(usage[output_token_name])
85
- except Exception as e:
86
- return f"Could not fetch tokens from model response: {e}"
87
-
88
- # Extract price information
89
- try:
90
- inverse_output_price = relevant_prices["output"]["one_usd_buys"]
91
- inverse_input_price = relevant_prices["input"]["one_usd_buys"]
92
- except Exception as e:
93
- if "output" not in relevant_prices:
94
- return f"Could not fetch prices from {relevant_prices} - {e}; Missing 'output' key."
95
- if "input" not in relevant_prices:
96
- return f"Could not fetch prices from {relevant_prices} - {e}; Missing 'input' key."
97
- return f"Could not fetch prices from {relevant_prices} - {e}"
98
-
99
- # Calculate input cost
100
- if inverse_input_price == "infinity":
101
- input_cost = 0
102
- else:
103
- try:
104
- input_cost = input_tokens / float(inverse_input_price)
105
- except Exception as e:
106
- return f"Could not compute input price - {e}."
107
-
108
- # Calculate output cost
109
- if inverse_output_price == "infinity":
110
- output_cost = 0
111
- else:
112
- try:
113
- output_cost = output_tokens / float(inverse_output_price)
114
- except Exception as e:
115
- return f"Could not compute output price - {e}"
116
-
117
- return input_cost + output_cost
118
-
119
- @property
120
- def is_initialized(self) -> bool:
121
- """
122
- Check if the PriceManager has been initialized.
123
-
124
- Returns:
125
- bool: True if initialized, False otherwise
126
- """
127
- return self._is_initialized
@@ -1,106 +0,0 @@
1
- import json
2
- from typing import Optional, Any, List
3
- from edsl.exceptions.language_models import LanguageModelBadResponseError
4
-
5
- from json_repair import repair_json
6
-
7
-
8
- def _extract_item_from_raw_response(data, sequence):
9
- if isinstance(data, str):
10
- try:
11
- data = json.loads(data)
12
- except json.JSONDecodeError as e:
13
- return data
14
- current_data = data
15
- for i, key in enumerate(sequence):
16
- try:
17
- if isinstance(current_data, (list, tuple)):
18
- if not isinstance(key, int):
19
- raise TypeError(
20
- f"Expected integer index for sequence at position {i}, got {type(key).__name__}"
21
- )
22
- if key < 0 or key >= len(current_data):
23
- raise IndexError(
24
- f"Index {key} out of range for sequence of length {len(current_data)} at position {i}"
25
- )
26
- elif isinstance(current_data, dict):
27
- if key not in current_data:
28
- raise KeyError(
29
- f"Key '{key}' not found in dictionary at position {i}"
30
- )
31
- else:
32
- raise TypeError(
33
- f"Cannot index into {type(current_data).__name__} at position {i}. Full response is: {data} of type {type(data)}. Key sequence is: {sequence}"
34
- )
35
-
36
- current_data = current_data[key]
37
- except Exception as e:
38
- path = " -> ".join(map(str, sequence[: i + 1]))
39
- if "error" in data:
40
- msg = data["error"]
41
- else:
42
- msg = f"Error accessing path: {path}. {str(e)}. Full response is: '{data}'"
43
- raise LanguageModelBadResponseError(message=msg, response_json=data)
44
- if isinstance(current_data, str):
45
- return current_data.strip()
46
- else:
47
- return current_data
48
-
49
-
50
- class RawResponseHandler:
51
- """Class to handle raw responses from language models."""
52
-
53
- def __init__(self, key_sequence: list, usage_sequence: Optional[list] = None):
54
- self.key_sequence = key_sequence
55
- self.usage_sequence = usage_sequence
56
-
57
- def get_generated_token_string(self, raw_response):
58
- return _extract_item_from_raw_response(raw_response, self.key_sequence)
59
-
60
- def get_usage_dict(self, raw_response):
61
- if self.usage_sequence is None:
62
- return {}
63
- return _extract_item_from_raw_response(raw_response, self.usage_sequence)
64
-
65
- def parse_response(self, raw_response: dict[str, Any]) -> "EDSLOutput":
66
- """Parses the API response and returns the response text."""
67
-
68
- from edsl.data_transfer_models import EDSLOutput
69
-
70
- generated_token_string = self.get_generated_token_string(raw_response)
71
- last_newline = generated_token_string.rfind("\n")
72
-
73
- if last_newline == -1:
74
- # There is no comment
75
- edsl_dict = {
76
- "answer": self.convert_answer(generated_token_string),
77
- "generated_tokens": generated_token_string,
78
- "comment": None,
79
- }
80
- else:
81
- edsl_dict = {
82
- "answer": self.convert_answer(generated_token_string[:last_newline]),
83
- "comment": generated_token_string[last_newline + 1 :].strip(),
84
- "generated_tokens": generated_token_string,
85
- }
86
- return EDSLOutput(**edsl_dict)
87
-
88
- @staticmethod
89
- def convert_answer(response_part):
90
- import json
91
-
92
- response_part = response_part.strip()
93
-
94
- if response_part == "None":
95
- return None
96
-
97
- repaired = repair_json(response_part)
98
- if repaired == '""':
99
- # it was a literal string
100
- return response_part
101
-
102
- try:
103
- return json.loads(repaired)
104
- except json.JSONDecodeError as j:
105
- # last resort
106
- return response_part
File without changes
@@ -1,63 +0,0 @@
1
- from collections import UserDict
2
- from dataclasses import asdict
3
- from edsl.enums import service_to_api_keyname
4
-
5
- from edsl.language_models.key_management.models import LanguageModelInput
6
-
7
-
8
- class KeyLookup(UserDict):
9
- """A class for looking up API keys and related configuration.
10
-
11
- >>> from edsl.language_models.key_management.models import LanguageModelInput
12
- >>> lookup = KeyLookup()
13
- >>> lm_input = LanguageModelInput.example()
14
- >>> lookup['test'] = lm_input
15
- >>> lookup.to_dict()['test']['api_token']
16
- 'sk-abcd123'
17
- >>> restored = KeyLookup.from_dict(lookup.to_dict())
18
- >>> restored['test'].api_token
19
- 'sk-abcd123'
20
- """
21
-
22
- def to_dict(self):
23
- """
24
- >>> kl = KeyLookup.example()
25
- >>> kl2 = KeyLookup.from_dict(kl.to_dict())
26
- >>> kl2 == kl
27
- True
28
- >>> kl2 is kl
29
- False
30
- """
31
- return {k: asdict(v) for k, v in self.data.items()}
32
-
33
- @classmethod
34
- def from_dict(cls, d):
35
- return cls({k: LanguageModelInput(**v) for k, v in d.items()})
36
-
37
- @classmethod
38
- def example(cls):
39
- return cls(
40
- {
41
- "test": LanguageModelInput.example(),
42
- "openai": LanguageModelInput.example(),
43
- }
44
- )
45
-
46
- def to_dot_env(self):
47
- """Return a string representation of the key lookup collection for a .env file."""
48
- lines = []
49
- for service, lm_input in self.items():
50
- if service != "test":
51
- lines.append(f"EDSL_SERVICE_RPM_{service.upper()}={lm_input.rpm}")
52
- lines.append(f"EDSL_SERVICE_TPM_{service.upper()}={lm_input.tpm}")
53
- key_name = service_to_api_keyname.get(service, service)
54
- lines.append(f"{key_name.upper()}={lm_input.api_token}")
55
- if lm_input.api_id is not None:
56
- lines.append(f"{service.upper()}_API_ID={lm_input.api_id}")
57
- return "\n".join([f"{line}" for line in lines])
58
-
59
-
60
- if __name__ == "__main__":
61
- import doctest
62
-
63
- doctest.testmod(optionflags=doctest.ELLIPSIS)