edsl 0.1.15__py3-none-any.whl → 0.1.40__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 (407) hide show
  1. edsl/Base.py +348 -38
  2. edsl/BaseDiff.py +260 -0
  3. edsl/TemplateLoader.py +24 -0
  4. edsl/__init__.py +45 -10
  5. edsl/__version__.py +1 -1
  6. edsl/agents/Agent.py +842 -144
  7. edsl/agents/AgentList.py +521 -25
  8. edsl/agents/Invigilator.py +250 -374
  9. edsl/agents/InvigilatorBase.py +257 -0
  10. edsl/agents/PromptConstructor.py +272 -0
  11. edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
  12. edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
  13. edsl/agents/descriptors.py +43 -13
  14. edsl/agents/prompt_helpers.py +129 -0
  15. edsl/agents/question_option_processor.py +172 -0
  16. edsl/auto/AutoStudy.py +130 -0
  17. edsl/auto/StageBase.py +243 -0
  18. edsl/auto/StageGenerateSurvey.py +178 -0
  19. edsl/auto/StageLabelQuestions.py +125 -0
  20. edsl/auto/StagePersona.py +61 -0
  21. edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
  22. edsl/auto/StagePersonaDimensionValues.py +74 -0
  23. edsl/auto/StagePersonaDimensions.py +69 -0
  24. edsl/auto/StageQuestions.py +74 -0
  25. edsl/auto/SurveyCreatorPipeline.py +21 -0
  26. edsl/auto/utilities.py +218 -0
  27. edsl/base/Base.py +279 -0
  28. edsl/config.py +115 -113
  29. edsl/conversation/Conversation.py +290 -0
  30. edsl/conversation/car_buying.py +59 -0
  31. edsl/conversation/chips.py +95 -0
  32. edsl/conversation/mug_negotiation.py +81 -0
  33. edsl/conversation/next_speaker_utilities.py +93 -0
  34. edsl/coop/CoopFunctionsMixin.py +15 -0
  35. edsl/coop/ExpectedParrotKeyHandler.py +125 -0
  36. edsl/coop/PriceFetcher.py +54 -0
  37. edsl/coop/__init__.py +1 -0
  38. edsl/coop/coop.py +1029 -134
  39. edsl/coop/utils.py +131 -0
  40. edsl/data/Cache.py +560 -89
  41. edsl/data/CacheEntry.py +230 -0
  42. edsl/data/CacheHandler.py +168 -0
  43. edsl/data/RemoteCacheSync.py +186 -0
  44. edsl/data/SQLiteDict.py +292 -0
  45. edsl/data/__init__.py +5 -3
  46. edsl/data/orm.py +6 -33
  47. edsl/data_transfer_models.py +74 -27
  48. edsl/enums.py +165 -8
  49. edsl/exceptions/BaseException.py +21 -0
  50. edsl/exceptions/__init__.py +52 -46
  51. edsl/exceptions/agents.py +33 -15
  52. edsl/exceptions/cache.py +5 -0
  53. edsl/exceptions/coop.py +8 -0
  54. edsl/exceptions/general.py +34 -0
  55. edsl/exceptions/inference_services.py +5 -0
  56. edsl/exceptions/jobs.py +15 -0
  57. edsl/exceptions/language_models.py +46 -1
  58. edsl/exceptions/questions.py +80 -5
  59. edsl/exceptions/results.py +16 -5
  60. edsl/exceptions/scenarios.py +29 -0
  61. edsl/exceptions/surveys.py +13 -10
  62. edsl/inference_services/AnthropicService.py +106 -0
  63. edsl/inference_services/AvailableModelCacheHandler.py +184 -0
  64. edsl/inference_services/AvailableModelFetcher.py +215 -0
  65. edsl/inference_services/AwsBedrock.py +118 -0
  66. edsl/inference_services/AzureAI.py +215 -0
  67. edsl/inference_services/DeepInfraService.py +18 -0
  68. edsl/inference_services/GoogleService.py +143 -0
  69. edsl/inference_services/GroqService.py +20 -0
  70. edsl/inference_services/InferenceServiceABC.py +80 -0
  71. edsl/inference_services/InferenceServicesCollection.py +138 -0
  72. edsl/inference_services/MistralAIService.py +120 -0
  73. edsl/inference_services/OllamaService.py +18 -0
  74. edsl/inference_services/OpenAIService.py +236 -0
  75. edsl/inference_services/PerplexityService.py +160 -0
  76. edsl/inference_services/ServiceAvailability.py +135 -0
  77. edsl/inference_services/TestService.py +90 -0
  78. edsl/inference_services/TogetherAIService.py +172 -0
  79. edsl/inference_services/data_structures.py +134 -0
  80. edsl/inference_services/models_available_cache.py +118 -0
  81. edsl/inference_services/rate_limits_cache.py +25 -0
  82. edsl/inference_services/registry.py +41 -0
  83. edsl/inference_services/write_available.py +10 -0
  84. edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
  85. edsl/jobs/Answers.py +21 -20
  86. edsl/jobs/FetchInvigilator.py +47 -0
  87. edsl/jobs/InterviewTaskManager.py +98 -0
  88. edsl/jobs/InterviewsConstructor.py +50 -0
  89. edsl/jobs/Jobs.py +684 -206
  90. edsl/jobs/JobsChecks.py +172 -0
  91. edsl/jobs/JobsComponentConstructor.py +189 -0
  92. edsl/jobs/JobsPrompts.py +270 -0
  93. edsl/jobs/JobsRemoteInferenceHandler.py +311 -0
  94. edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
  95. edsl/jobs/RequestTokenEstimator.py +30 -0
  96. edsl/jobs/async_interview_runner.py +138 -0
  97. edsl/jobs/buckets/BucketCollection.py +104 -0
  98. edsl/jobs/buckets/ModelBuckets.py +65 -0
  99. edsl/jobs/buckets/TokenBucket.py +283 -0
  100. edsl/jobs/buckets/TokenBucketAPI.py +211 -0
  101. edsl/jobs/buckets/TokenBucketClient.py +191 -0
  102. edsl/jobs/check_survey_scenario_compatibility.py +85 -0
  103. edsl/jobs/data_structures.py +120 -0
  104. edsl/jobs/decorators.py +35 -0
  105. edsl/jobs/interviews/Interview.py +392 -0
  106. edsl/jobs/interviews/InterviewExceptionCollection.py +99 -0
  107. edsl/jobs/interviews/InterviewExceptionEntry.py +186 -0
  108. edsl/jobs/interviews/InterviewStatistic.py +63 -0
  109. edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -0
  110. edsl/jobs/interviews/InterviewStatusDictionary.py +78 -0
  111. edsl/jobs/interviews/InterviewStatusLog.py +92 -0
  112. edsl/jobs/interviews/ReportErrors.py +66 -0
  113. edsl/jobs/interviews/interview_status_enum.py +9 -0
  114. edsl/jobs/jobs_status_enums.py +9 -0
  115. edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
  116. edsl/jobs/results_exceptions_handler.py +98 -0
  117. edsl/jobs/runners/JobsRunnerAsyncio.py +151 -110
  118. edsl/jobs/runners/JobsRunnerStatus.py +298 -0
  119. edsl/jobs/tasks/QuestionTaskCreator.py +244 -0
  120. edsl/jobs/tasks/TaskCreators.py +64 -0
  121. edsl/jobs/tasks/TaskHistory.py +470 -0
  122. edsl/jobs/tasks/TaskStatusLog.py +23 -0
  123. edsl/jobs/tasks/task_status_enum.py +161 -0
  124. edsl/jobs/tokens/InterviewTokenUsage.py +27 -0
  125. edsl/jobs/tokens/TokenUsage.py +34 -0
  126. edsl/language_models/ComputeCost.py +63 -0
  127. edsl/language_models/LanguageModel.py +507 -386
  128. edsl/language_models/ModelList.py +164 -0
  129. edsl/language_models/PriceManager.py +127 -0
  130. edsl/language_models/RawResponseHandler.py +106 -0
  131. edsl/language_models/RegisterLanguageModelsMeta.py +184 -0
  132. edsl/language_models/__init__.py +1 -8
  133. edsl/language_models/fake_openai_call.py +15 -0
  134. edsl/language_models/fake_openai_service.py +61 -0
  135. edsl/language_models/key_management/KeyLookup.py +63 -0
  136. edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
  137. edsl/language_models/key_management/KeyLookupCollection.py +38 -0
  138. edsl/language_models/key_management/__init__.py +0 -0
  139. edsl/language_models/key_management/models.py +131 -0
  140. edsl/language_models/model.py +256 -0
  141. edsl/language_models/repair.py +109 -41
  142. edsl/language_models/utilities.py +65 -0
  143. edsl/notebooks/Notebook.py +263 -0
  144. edsl/notebooks/NotebookToLaTeX.py +142 -0
  145. edsl/notebooks/__init__.py +1 -0
  146. edsl/prompts/Prompt.py +222 -93
  147. edsl/prompts/__init__.py +1 -1
  148. edsl/questions/ExceptionExplainer.py +77 -0
  149. edsl/questions/HTMLQuestion.py +103 -0
  150. edsl/questions/QuestionBase.py +518 -0
  151. edsl/questions/QuestionBasePromptsMixin.py +221 -0
  152. edsl/questions/QuestionBudget.py +164 -67
  153. edsl/questions/QuestionCheckBox.py +281 -62
  154. edsl/questions/QuestionDict.py +343 -0
  155. edsl/questions/QuestionExtract.py +136 -50
  156. edsl/questions/QuestionFreeText.py +79 -55
  157. edsl/questions/QuestionFunctional.py +138 -41
  158. edsl/questions/QuestionList.py +184 -57
  159. edsl/questions/QuestionMatrix.py +265 -0
  160. edsl/questions/QuestionMultipleChoice.py +293 -69
  161. edsl/questions/QuestionNumerical.py +109 -56
  162. edsl/questions/QuestionRank.py +244 -49
  163. edsl/questions/Quick.py +41 -0
  164. edsl/questions/SimpleAskMixin.py +74 -0
  165. edsl/questions/__init__.py +9 -6
  166. edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +153 -38
  167. edsl/questions/compose_questions.py +13 -7
  168. edsl/questions/data_structures.py +20 -0
  169. edsl/questions/decorators.py +21 -0
  170. edsl/questions/derived/QuestionLikertFive.py +28 -26
  171. edsl/questions/derived/QuestionLinearScale.py +41 -28
  172. edsl/questions/derived/QuestionTopK.py +34 -26
  173. edsl/questions/derived/QuestionYesNo.py +40 -27
  174. edsl/questions/descriptors.py +228 -74
  175. edsl/questions/loop_processor.py +149 -0
  176. edsl/questions/prompt_templates/question_budget.jinja +13 -0
  177. edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
  178. edsl/questions/prompt_templates/question_extract.jinja +11 -0
  179. edsl/questions/prompt_templates/question_free_text.jinja +3 -0
  180. edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
  181. edsl/questions/prompt_templates/question_list.jinja +17 -0
  182. edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
  183. edsl/questions/prompt_templates/question_numerical.jinja +37 -0
  184. edsl/questions/question_base_gen_mixin.py +168 -0
  185. edsl/questions/question_registry.py +130 -46
  186. edsl/questions/register_questions_meta.py +71 -0
  187. edsl/questions/response_validator_abc.py +188 -0
  188. edsl/questions/response_validator_factory.py +34 -0
  189. edsl/questions/settings.py +5 -2
  190. edsl/questions/templates/__init__.py +0 -0
  191. edsl/questions/templates/budget/__init__.py +0 -0
  192. edsl/questions/templates/budget/answering_instructions.jinja +7 -0
  193. edsl/questions/templates/budget/question_presentation.jinja +7 -0
  194. edsl/questions/templates/checkbox/__init__.py +0 -0
  195. edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
  196. edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
  197. edsl/questions/templates/dict/__init__.py +0 -0
  198. edsl/questions/templates/dict/answering_instructions.jinja +21 -0
  199. edsl/questions/templates/dict/question_presentation.jinja +1 -0
  200. edsl/questions/templates/extract/__init__.py +0 -0
  201. edsl/questions/templates/extract/answering_instructions.jinja +7 -0
  202. edsl/questions/templates/extract/question_presentation.jinja +1 -0
  203. edsl/questions/templates/free_text/__init__.py +0 -0
  204. edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
  205. edsl/questions/templates/free_text/question_presentation.jinja +1 -0
  206. edsl/questions/templates/likert_five/__init__.py +0 -0
  207. edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
  208. edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
  209. edsl/questions/templates/linear_scale/__init__.py +0 -0
  210. edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
  211. edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
  212. edsl/questions/templates/list/__init__.py +0 -0
  213. edsl/questions/templates/list/answering_instructions.jinja +4 -0
  214. edsl/questions/templates/list/question_presentation.jinja +5 -0
  215. edsl/questions/templates/matrix/__init__.py +1 -0
  216. edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
  217. edsl/questions/templates/matrix/question_presentation.jinja +20 -0
  218. edsl/questions/templates/multiple_choice/__init__.py +0 -0
  219. edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
  220. edsl/questions/templates/multiple_choice/html.jinja +0 -0
  221. edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
  222. edsl/questions/templates/numerical/__init__.py +0 -0
  223. edsl/questions/templates/numerical/answering_instructions.jinja +7 -0
  224. edsl/questions/templates/numerical/question_presentation.jinja +7 -0
  225. edsl/questions/templates/rank/__init__.py +0 -0
  226. edsl/questions/templates/rank/answering_instructions.jinja +11 -0
  227. edsl/questions/templates/rank/question_presentation.jinja +15 -0
  228. edsl/questions/templates/top_k/__init__.py +0 -0
  229. edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
  230. edsl/questions/templates/top_k/question_presentation.jinja +22 -0
  231. edsl/questions/templates/yes_no/__init__.py +0 -0
  232. edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
  233. edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
  234. edsl/results/CSSParameterizer.py +108 -0
  235. edsl/results/Dataset.py +550 -19
  236. edsl/results/DatasetExportMixin.py +594 -0
  237. edsl/results/DatasetTree.py +295 -0
  238. edsl/results/MarkdownToDocx.py +122 -0
  239. edsl/results/MarkdownToPDF.py +111 -0
  240. edsl/results/Result.py +477 -173
  241. edsl/results/Results.py +987 -269
  242. edsl/results/ResultsExportMixin.py +28 -125
  243. edsl/results/ResultsGGMixin.py +83 -15
  244. edsl/results/TableDisplay.py +125 -0
  245. edsl/results/TextEditor.py +50 -0
  246. edsl/results/__init__.py +1 -1
  247. edsl/results/file_exports.py +252 -0
  248. edsl/results/results_fetch_mixin.py +33 -0
  249. edsl/results/results_selector.py +145 -0
  250. edsl/results/results_tools_mixin.py +98 -0
  251. edsl/results/smart_objects.py +96 -0
  252. edsl/results/table_data_class.py +12 -0
  253. edsl/results/table_display.css +78 -0
  254. edsl/results/table_renderers.py +118 -0
  255. edsl/results/tree_explore.py +115 -0
  256. edsl/scenarios/ConstructDownloadLink.py +109 -0
  257. edsl/scenarios/DocumentChunker.py +102 -0
  258. edsl/scenarios/DocxScenario.py +16 -0
  259. edsl/scenarios/FileStore.py +543 -0
  260. edsl/scenarios/PdfExtractor.py +40 -0
  261. edsl/scenarios/Scenario.py +431 -62
  262. edsl/scenarios/ScenarioHtmlMixin.py +65 -0
  263. edsl/scenarios/ScenarioList.py +1415 -45
  264. edsl/scenarios/ScenarioListExportMixin.py +45 -0
  265. edsl/scenarios/ScenarioListPdfMixin.py +239 -0
  266. edsl/scenarios/__init__.py +2 -0
  267. edsl/scenarios/directory_scanner.py +96 -0
  268. edsl/scenarios/file_methods.py +85 -0
  269. edsl/scenarios/handlers/__init__.py +13 -0
  270. edsl/scenarios/handlers/csv.py +49 -0
  271. edsl/scenarios/handlers/docx.py +76 -0
  272. edsl/scenarios/handlers/html.py +37 -0
  273. edsl/scenarios/handlers/json.py +111 -0
  274. edsl/scenarios/handlers/latex.py +5 -0
  275. edsl/scenarios/handlers/md.py +51 -0
  276. edsl/scenarios/handlers/pdf.py +68 -0
  277. edsl/scenarios/handlers/png.py +39 -0
  278. edsl/scenarios/handlers/pptx.py +105 -0
  279. edsl/scenarios/handlers/py.py +294 -0
  280. edsl/scenarios/handlers/sql.py +313 -0
  281. edsl/scenarios/handlers/sqlite.py +149 -0
  282. edsl/scenarios/handlers/txt.py +33 -0
  283. edsl/scenarios/scenario_join.py +131 -0
  284. edsl/scenarios/scenario_selector.py +156 -0
  285. edsl/shared.py +1 -0
  286. edsl/study/ObjectEntry.py +173 -0
  287. edsl/study/ProofOfWork.py +113 -0
  288. edsl/study/SnapShot.py +80 -0
  289. edsl/study/Study.py +521 -0
  290. edsl/study/__init__.py +4 -0
  291. edsl/surveys/ConstructDAG.py +92 -0
  292. edsl/surveys/DAG.py +92 -11
  293. edsl/surveys/EditSurvey.py +221 -0
  294. edsl/surveys/InstructionHandler.py +100 -0
  295. edsl/surveys/Memory.py +9 -4
  296. edsl/surveys/MemoryManagement.py +72 -0
  297. edsl/surveys/MemoryPlan.py +156 -35
  298. edsl/surveys/Rule.py +221 -74
  299. edsl/surveys/RuleCollection.py +241 -61
  300. edsl/surveys/RuleManager.py +172 -0
  301. edsl/surveys/Simulator.py +75 -0
  302. edsl/surveys/Survey.py +1079 -339
  303. edsl/surveys/SurveyCSS.py +273 -0
  304. edsl/surveys/SurveyExportMixin.py +235 -40
  305. edsl/surveys/SurveyFlowVisualization.py +181 -0
  306. edsl/surveys/SurveyQualtricsImport.py +284 -0
  307. edsl/surveys/SurveyToApp.py +141 -0
  308. edsl/surveys/__init__.py +4 -2
  309. edsl/surveys/base.py +19 -3
  310. edsl/surveys/descriptors.py +17 -6
  311. edsl/surveys/instructions/ChangeInstruction.py +48 -0
  312. edsl/surveys/instructions/Instruction.py +56 -0
  313. edsl/surveys/instructions/InstructionCollection.py +82 -0
  314. edsl/surveys/instructions/__init__.py +0 -0
  315. edsl/templates/error_reporting/base.html +24 -0
  316. edsl/templates/error_reporting/exceptions_by_model.html +35 -0
  317. edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
  318. edsl/templates/error_reporting/exceptions_by_type.html +17 -0
  319. edsl/templates/error_reporting/interview_details.html +116 -0
  320. edsl/templates/error_reporting/interviews.html +19 -0
  321. edsl/templates/error_reporting/overview.html +5 -0
  322. edsl/templates/error_reporting/performance_plot.html +2 -0
  323. edsl/templates/error_reporting/report.css +74 -0
  324. edsl/templates/error_reporting/report.html +118 -0
  325. edsl/templates/error_reporting/report.js +25 -0
  326. edsl/tools/__init__.py +1 -0
  327. edsl/tools/clusters.py +192 -0
  328. edsl/tools/embeddings.py +27 -0
  329. edsl/tools/embeddings_plotting.py +118 -0
  330. edsl/tools/plotting.py +112 -0
  331. edsl/tools/summarize.py +18 -0
  332. edsl/utilities/PrettyList.py +56 -0
  333. edsl/utilities/SystemInfo.py +5 -0
  334. edsl/utilities/__init__.py +21 -20
  335. edsl/utilities/ast_utilities.py +3 -0
  336. edsl/utilities/data/Registry.py +2 -0
  337. edsl/utilities/decorators.py +41 -0
  338. edsl/utilities/gcp_bucket/__init__.py +0 -0
  339. edsl/utilities/gcp_bucket/cloud_storage.py +96 -0
  340. edsl/utilities/interface.py +310 -60
  341. edsl/utilities/is_notebook.py +18 -0
  342. edsl/utilities/is_valid_variable_name.py +11 -0
  343. edsl/utilities/naming_utilities.py +263 -0
  344. edsl/utilities/remove_edsl_version.py +24 -0
  345. edsl/utilities/repair_functions.py +28 -0
  346. edsl/utilities/restricted_python.py +70 -0
  347. edsl/utilities/utilities.py +203 -13
  348. edsl-0.1.40.dist-info/METADATA +111 -0
  349. edsl-0.1.40.dist-info/RECORD +362 -0
  350. {edsl-0.1.15.dist-info → edsl-0.1.40.dist-info}/WHEEL +1 -1
  351. edsl/agents/AgentListExportMixin.py +0 -24
  352. edsl/coop/old.py +0 -31
  353. edsl/data/Database.py +0 -141
  354. edsl/data/crud.py +0 -121
  355. edsl/jobs/Interview.py +0 -435
  356. edsl/jobs/JobsRunner.py +0 -63
  357. edsl/jobs/JobsRunnerStatusMixin.py +0 -115
  358. edsl/jobs/base.py +0 -47
  359. edsl/jobs/buckets.py +0 -178
  360. edsl/jobs/runners/JobsRunnerDryRun.py +0 -19
  361. edsl/jobs/runners/JobsRunnerStreaming.py +0 -54
  362. edsl/jobs/task_management.py +0 -215
  363. edsl/jobs/token_tracking.py +0 -78
  364. edsl/language_models/DeepInfra.py +0 -69
  365. edsl/language_models/OpenAI.py +0 -98
  366. edsl/language_models/model_interfaces/GeminiPro.py +0 -66
  367. edsl/language_models/model_interfaces/LanguageModelOpenAIFour.py +0 -8
  368. edsl/language_models/model_interfaces/LanguageModelOpenAIThreeFiveTurbo.py +0 -8
  369. edsl/language_models/model_interfaces/LlamaTwo13B.py +0 -21
  370. edsl/language_models/model_interfaces/LlamaTwo70B.py +0 -21
  371. edsl/language_models/model_interfaces/Mixtral8x7B.py +0 -24
  372. edsl/language_models/registry.py +0 -81
  373. edsl/language_models/schemas.py +0 -15
  374. edsl/language_models/unused/ReplicateBase.py +0 -83
  375. edsl/prompts/QuestionInstructionsBase.py +0 -6
  376. edsl/prompts/library/agent_instructions.py +0 -29
  377. edsl/prompts/library/agent_persona.py +0 -17
  378. edsl/prompts/library/question_budget.py +0 -26
  379. edsl/prompts/library/question_checkbox.py +0 -32
  380. edsl/prompts/library/question_extract.py +0 -19
  381. edsl/prompts/library/question_freetext.py +0 -14
  382. edsl/prompts/library/question_linear_scale.py +0 -20
  383. edsl/prompts/library/question_list.py +0 -22
  384. edsl/prompts/library/question_multiple_choice.py +0 -44
  385. edsl/prompts/library/question_numerical.py +0 -31
  386. edsl/prompts/library/question_rank.py +0 -21
  387. edsl/prompts/prompt_config.py +0 -33
  388. edsl/prompts/registry.py +0 -185
  389. edsl/questions/Question.py +0 -240
  390. edsl/report/InputOutputDataTypes.py +0 -134
  391. edsl/report/RegressionMixin.py +0 -28
  392. edsl/report/ReportOutputs.py +0 -1228
  393. edsl/report/ResultsFetchMixin.py +0 -106
  394. edsl/report/ResultsOutputMixin.py +0 -14
  395. edsl/report/demo.ipynb +0 -645
  396. edsl/results/ResultsDBMixin.py +0 -184
  397. edsl/surveys/SurveyFlowVisualizationMixin.py +0 -92
  398. edsl/trackers/Tracker.py +0 -91
  399. edsl/trackers/TrackerAPI.py +0 -196
  400. edsl/trackers/TrackerTasks.py +0 -70
  401. edsl/utilities/pastebin.py +0 -141
  402. edsl-0.1.15.dist-info/METADATA +0 -69
  403. edsl-0.1.15.dist-info/RECORD +0 -142
  404. /edsl/{language_models/model_interfaces → inference_services}/__init__.py +0 -0
  405. /edsl/{report/__init__.py → jobs/runners/JobsRunnerStatusData.py} +0 -0
  406. /edsl/{trackers/__init__.py → language_models/ServiceDataSources.py} +0 -0
  407. {edsl-0.1.15.dist-info → edsl-0.1.40.dist-info}/LICENSE +0 -0
@@ -0,0 +1,470 @@
1
+ from typing import List, Optional
2
+ from io import BytesIO
3
+ import base64
4
+ from edsl.jobs.tasks.task_status_enum import TaskStatus
5
+ from edsl.Base import RepresentationMixin
6
+
7
+
8
+ class TaskHistory(RepresentationMixin):
9
+ def __init__(
10
+ self,
11
+ interviews: List["Interview"] = None,
12
+ include_traceback: bool = False,
13
+ max_interviews: int = 10,
14
+ interviews_with_exceptions_only: bool = False,
15
+ ):
16
+ """
17
+ The structure of a TaskHistory exception
18
+
19
+ [Interview.exceptions, Interview.exceptions, Interview.exceptions, ...]
20
+
21
+ >>> _ = TaskHistory.example()
22
+ ...
23
+ """
24
+ self.interviews_with_exceptions_only = interviews_with_exceptions_only
25
+ self._interviews = {}
26
+ self.total_interviews = []
27
+ if interviews is not None:
28
+ for interview in interviews:
29
+ self.add_interview(interview)
30
+
31
+ self.include_traceback = include_traceback
32
+ self._interviews = {
33
+ index: interview for index, interview in enumerate(self.total_interviews)
34
+ }
35
+ self.max_interviews = max_interviews
36
+
37
+ # self.total_interviews = interviews
38
+ self.include_traceback = include_traceback
39
+
40
+ # self._interviews = {index: i for index, i in enumerate(self.total_interviews)}
41
+ self.max_interviews = max_interviews
42
+
43
+ def add_interview(self, interview: "Interview"):
44
+ """Add a single interview to the history"""
45
+ if self.interviews_with_exceptions_only and interview.exceptions == {}:
46
+ return
47
+
48
+ self.total_interviews.append(interview)
49
+ self._interviews[len(self._interviews)] = interview
50
+
51
+ @classmethod
52
+ def example(cls):
53
+ """ """
54
+ from edsl.jobs.interviews.Interview import Interview
55
+
56
+ from edsl.jobs.Jobs import Jobs
57
+
58
+ j = Jobs.example(throw_exception_probability=1, test_model=True)
59
+
60
+ from edsl.config import CONFIG
61
+
62
+ results = j.run(
63
+ print_exceptions=False,
64
+ skip_retry=True,
65
+ cache=False,
66
+ raise_validation_errors=True,
67
+ disable_remote_cache=True,
68
+ disable_remote_inference=True,
69
+ )
70
+
71
+ return cls(results.task_history.total_interviews)
72
+
73
+ @property
74
+ def exceptions(self):
75
+ """
76
+ >>> len(TaskHistory.example().exceptions)
77
+ 4
78
+ """
79
+ return [i.exceptions for k, i in self._interviews.items() if i.exceptions != {}]
80
+
81
+ @property
82
+ def unfixed_exceptions(self):
83
+ """
84
+ >>> len(TaskHistory.example().unfixed_exceptions)
85
+ 4
86
+ """
87
+ return [
88
+ i.exceptions
89
+ for k, i in self._interviews.items()
90
+ if i.exceptions.num_unfixed() > 0
91
+ ]
92
+
93
+ @property
94
+ def indices(self):
95
+ return [k for k, i in self._interviews.items() if i.exceptions != {}]
96
+
97
+ def __repr__(self):
98
+ """Return a string representation of the TaskHistory."""
99
+ return f"TaskHistory(interviews={self.total_interviews})."
100
+
101
+ def to_dict(self, add_edsl_version=True):
102
+ """Return the TaskHistory as a dictionary."""
103
+ d = {
104
+ "interviews": [
105
+ i.to_dict(add_edsl_version=add_edsl_version)
106
+ for i in self.total_interviews
107
+ ],
108
+ "include_traceback": self.include_traceback,
109
+ }
110
+ if add_edsl_version:
111
+ from edsl import __version__
112
+
113
+ d["edsl_version"] = __version__
114
+ d["edsl_class_name"] = "TaskHistory"
115
+ return d
116
+
117
+ @classmethod
118
+ def from_dict(cls, data: dict):
119
+ """Create a TaskHistory from a dictionary."""
120
+ if data is None:
121
+ return cls([], include_traceback=False)
122
+
123
+ from edsl.jobs.interviews.Interview import Interview
124
+
125
+ interviews = [Interview.from_dict(i) for i in data["interviews"]]
126
+ return cls(interviews, include_traceback=data["include_traceback"])
127
+
128
+ @property
129
+ def has_exceptions(self) -> bool:
130
+ """Return True if there are any exceptions.
131
+
132
+ >>> TaskHistory.example().has_exceptions
133
+ True
134
+
135
+ """
136
+ return len(self.exceptions) > 0
137
+
138
+ @property
139
+ def has_unfixed_exceptions(self) -> bool:
140
+ """Return True if there are any exceptions."""
141
+ return len(self.unfixed_exceptions) > 0
142
+
143
+ def show_exceptions(self, tracebacks=False):
144
+ """Print the exceptions."""
145
+ for index in self.indices:
146
+ self.total_interviews[index].exceptions.print(tracebacks)
147
+
148
+ def get_updates(self):
149
+ """Return a list of all the updates."""
150
+ updates = []
151
+ for interview in self.total_interviews:
152
+ for question_name, logs in interview.task_status_logs.items():
153
+ updates.append(logs)
154
+ return updates
155
+
156
+ def print(self):
157
+ from rich import print
158
+
159
+ print(self.get_updates())
160
+
161
+ def plot_completion_times(self):
162
+ """Plot the completion times for each task."""
163
+ import matplotlib.pyplot as plt
164
+
165
+ updates = self.get_updates()
166
+
167
+ elapsed = [update.max_time - update.min_time for update in updates]
168
+ for i, update in enumerate(updates):
169
+ if update[-1]["value"] != TaskStatus.SUCCESS:
170
+ elapsed[i] = 0
171
+ x = range(len(elapsed))
172
+ y = elapsed
173
+
174
+ plt.bar(x, y)
175
+ plt.title("Per-interview completion times")
176
+ plt.xlabel("Task")
177
+ plt.ylabel("Time (seconds)")
178
+ plt.show()
179
+
180
+ def plotting_data(self, num_periods=100):
181
+ updates = self.get_updates()
182
+
183
+ min_t = min([update.min_time for update in updates])
184
+ max_t = max([update.max_time for update in updates])
185
+ delta_t = (max_t - min_t) / (num_periods * 1.0)
186
+ time_periods = [min_t + delta_t * i for i in range(num_periods)]
187
+
188
+ def counts(t):
189
+ d = {}
190
+ for update in updates:
191
+ status = update.status_at_time(t)
192
+ if status in d:
193
+ d[status] += 1
194
+ else:
195
+ d[status] = 1
196
+ return d
197
+
198
+ status_counts = [counts(t) for t in time_periods]
199
+
200
+ new_counts = []
201
+ for status_count in status_counts:
202
+ d = {task_status: 0 for task_status in TaskStatus}
203
+ d.update(status_count)
204
+ new_counts.append(d)
205
+
206
+ return new_counts
207
+
208
+ def plot(self, num_periods=100, get_embedded_html=False):
209
+ """Plot the number of tasks in each state over time."""
210
+ new_counts = self.plotting_data(num_periods)
211
+ max_count = max([max(entry.values()) for entry in new_counts])
212
+
213
+ rows = int(len(TaskStatus) ** 0.5) + 1
214
+ cols = (len(TaskStatus) + rows - 1) // rows # Ensure all plots fit
215
+
216
+ import matplotlib.pyplot as plt
217
+
218
+ fig, axes = plt.subplots(rows, cols, figsize=(15, 10))
219
+ axes = axes.flatten() # Flatten in case of a single row/column
220
+
221
+ for i, status in enumerate(TaskStatus):
222
+ ax = axes[i]
223
+ x = range(len(new_counts))
224
+ y = [
225
+ item.get(status, 0) for item in new_counts
226
+ ] # Use .get() to handle missing keys safely
227
+ ax.plot(x, y, marker="o", linestyle="-")
228
+ ax.set_title(status.name)
229
+ ax.set_xlabel("Time Periods")
230
+ ax.set_ylabel("Count")
231
+ ax.grid(True)
232
+ ax.set_ylim(0, max_count)
233
+
234
+ # Hide any unused subplots
235
+ for ax in axes[len(TaskStatus) :]:
236
+ ax.axis("off")
237
+
238
+ plt.tight_layout()
239
+
240
+ if get_embedded_html:
241
+ buffer = BytesIO()
242
+ fig.savefig(buffer, format="png")
243
+ plt.close(fig)
244
+ buffer.seek(0)
245
+
246
+ # Encode plot to base64 string
247
+ img_data = base64.b64encode(buffer.getvalue()).decode("utf-8")
248
+ buffer.close()
249
+ return f'<img src="data:image/png;base64,{img_data}" alt="Plot">'
250
+ else:
251
+ plt.show()
252
+
253
+ def css(self):
254
+ from importlib import resources
255
+
256
+ env = resources.files("edsl").joinpath("templates/error_reporting")
257
+ css = env.joinpath("report.css").read_text()
258
+ return css
259
+
260
+ def javascript(self):
261
+ from importlib import resources
262
+
263
+ env = resources.files("edsl").joinpath("templates/error_reporting")
264
+ js = env.joinpath("report.js").read_text()
265
+ return js
266
+
267
+ @property
268
+ def exceptions_by_type(self) -> dict:
269
+ """Return a dictionary of exceptions by type."""
270
+ exceptions_by_type = {}
271
+ for interview in self.total_interviews:
272
+ for question_name, exceptions in interview.exceptions.items():
273
+ for exception in exceptions:
274
+ exception_type = exception.exception.__class__.__name__
275
+ if exception_type in exceptions_by_type:
276
+ exceptions_by_type[exception_type] += 1
277
+ else:
278
+ exceptions_by_type[exception_type] = 1
279
+ return exceptions_by_type
280
+
281
+ @property
282
+ def exceptions_by_service(self) -> dict:
283
+ """Return a dictionary of exceptions tallied by service."""
284
+ exceptions_by_service = {}
285
+ for interview in self.total_interviews:
286
+ service = interview.model._inference_service_
287
+ if service not in exceptions_by_service:
288
+ exceptions_by_service[service] = 0
289
+ if interview.exceptions != {}:
290
+ exceptions_by_service[service] += len(interview.exceptions)
291
+ return exceptions_by_service
292
+
293
+ @property
294
+ def exceptions_by_question_name(self) -> dict:
295
+ """Return a dictionary of exceptions tallied by question name."""
296
+ exceptions_by_question_name = {}
297
+ for interview in self.total_interviews:
298
+ for question_name, exceptions in interview.exceptions.items():
299
+ question_type = interview.survey._get_question_by_name(
300
+ question_name
301
+ ).question_type
302
+ if (question_name, question_type) not in exceptions_by_question_name:
303
+ exceptions_by_question_name[(question_name, question_type)] = 0
304
+ exceptions_by_question_name[(question_name, question_type)] += len(
305
+ exceptions
306
+ )
307
+
308
+ for question in self.total_interviews[0].survey.questions:
309
+ if (
310
+ question.question_name,
311
+ question.question_type,
312
+ ) not in exceptions_by_question_name:
313
+ exceptions_by_question_name[
314
+ (question.question_name, question.question_type)
315
+ ] = 0
316
+
317
+ sorted_exceptions_by_question_name = {
318
+ k: v
319
+ for k, v in sorted(
320
+ exceptions_by_question_name.items(),
321
+ key=lambda item: item[1],
322
+ reverse=True,
323
+ )
324
+ }
325
+ return sorted_exceptions_by_question_name
326
+
327
+ @property
328
+ def exceptions_by_model(self) -> dict:
329
+ """Return a dictionary of exceptions tallied by model and question name."""
330
+ exceptions_by_model = {}
331
+ for interview in self.total_interviews:
332
+ model = interview.model.model
333
+ service = interview.model._inference_service_
334
+ if (service, model) not in exceptions_by_model:
335
+ exceptions_by_model[(service, model)] = 0
336
+ if interview.exceptions != {}:
337
+ exceptions_by_model[(service, model)] += len(interview.exceptions)
338
+
339
+ # sort the exceptions by model
340
+ sorted_exceptions_by_model = {
341
+ k: v
342
+ for k, v in sorted(
343
+ exceptions_by_model.items(), key=lambda item: item[1], reverse=True
344
+ )
345
+ }
346
+ return sorted_exceptions_by_model
347
+
348
+ def generate_html_report(self, css: Optional[str], include_plot=False):
349
+ if include_plot:
350
+ performance_plot_html = self.plot(num_periods=100, get_embedded_html=True)
351
+ else:
352
+ performance_plot_html = ""
353
+
354
+ if css is None:
355
+ css = self.css()
356
+
357
+ models_used = set([i.model.model for index, i in self._interviews.items()])
358
+
359
+ from jinja2 import Environment, FileSystemLoader
360
+ from edsl.TemplateLoader import TemplateLoader
361
+
362
+ env = Environment(loader=TemplateLoader("edsl", "templates/error_reporting"))
363
+
364
+ # Get current memory usage at this point
365
+
366
+ template = env.get_template("base.html")
367
+
368
+ # Render the template with data
369
+ output = template.render(
370
+ interviews=self._interviews,
371
+ css=css,
372
+ javascript=self.javascript(),
373
+ num_exceptions=len(self.exceptions),
374
+ performance_plot_html=performance_plot_html,
375
+ exceptions_by_type=self.exceptions_by_type,
376
+ exceptions_by_question_name=self.exceptions_by_question_name,
377
+ exceptions_by_model=self.exceptions_by_model,
378
+ exceptions_by_service=self.exceptions_by_service,
379
+ models_used=models_used,
380
+ max_interviews=self.max_interviews,
381
+ )
382
+ return output
383
+
384
+ def html(
385
+ self,
386
+ filename: Optional[str] = None,
387
+ return_link=False,
388
+ css=None,
389
+ cta="Open Report in New Tab",
390
+ open_in_browser=False,
391
+ ):
392
+ """Return an HTML report."""
393
+
394
+ from IPython.display import display, HTML
395
+ import tempfile
396
+ import os
397
+ from edsl.utilities.utilities import is_notebook
398
+
399
+ output = self.generate_html_report(css)
400
+
401
+ # Save the rendered output to a file
402
+ with open("output.html", "w") as f:
403
+ f.write(output)
404
+
405
+ if filename is None:
406
+ current_directory = os.getcwd()
407
+ filename = tempfile.NamedTemporaryFile(
408
+ "w", delete=False, suffix=".html", dir=current_directory
409
+ ).name
410
+
411
+ with open(filename, "w") as f:
412
+ with open(filename, "w") as f:
413
+ f.write(output)
414
+
415
+ if is_notebook():
416
+ import html
417
+
418
+ html_url = f"/files/{filename}"
419
+ html_link = f'<a href="{html_url}" target="_blank">{cta}</a>'
420
+ display(HTML(html_link))
421
+ escaped_output = html.escape(output)
422
+ iframe = f""""
423
+ <iframe srcdoc="{ escaped_output }" style="width: 800px; height: 600px;"></iframe>
424
+ """
425
+ display(HTML(iframe))
426
+ else:
427
+ print(f"Exception report saved to {filename}")
428
+
429
+ if open_in_browser:
430
+ import webbrowser
431
+
432
+ webbrowser.open(f"file://{os.path.abspath(filename)}")
433
+
434
+ if return_link:
435
+ return filename
436
+
437
+ def notebook(self):
438
+ """Create a notebook with the HTML content embedded in the first cell, then delete the cell content while keeping the output."""
439
+ from nbformat import v4 as nbf
440
+ from nbconvert.preprocessors import ExecutePreprocessor
441
+ import nbformat
442
+ import os
443
+
444
+ # Use the existing html method to generate the HTML content
445
+ output_html = self.generate_html_report(css=None)
446
+ nb = nbf.new_notebook()
447
+
448
+ # Add a code cell that renders the HTML content
449
+ code_cell = nbf.new_code_cell(
450
+ f"""
451
+ from IPython.display import HTML, display
452
+ display(HTML('''{output_html}'''))
453
+ """
454
+ )
455
+ nb.cells.append(code_cell)
456
+
457
+ # Execute the notebook
458
+ ep = ExecutePreprocessor(timeout=600, kernel_name="python3")
459
+ ep.preprocess(nb, {"metadata": {"path": os.getcwd()}})
460
+
461
+ # After execution, clear the cell's source code
462
+ nb.cells[0].source = ""
463
+
464
+ return nb
465
+
466
+
467
+ if __name__ == "__main__":
468
+ import doctest
469
+
470
+ doctest.testmod(optionflags=doctest.ELLIPSIS)
@@ -0,0 +1,23 @@
1
+ from collections import UserList
2
+
3
+
4
+ class TaskStatusLog(UserList):
5
+ """A list of TaskStatusEntry objects."""
6
+
7
+ @property
8
+ def min_time(self):
9
+ return self[0]["log_time"]
10
+
11
+ @property
12
+ def max_time(self):
13
+ return self[-1]["log_time"]
14
+
15
+ def status_at_time(self, t):
16
+ """Return the status at time t.
17
+
18
+ TODO: Could re-factor with bisect to make this faster.
19
+ """
20
+ for entry in self:
21
+ if entry["log_time"] > t:
22
+ return entry["value"]
23
+ return self[-1]["value"]
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+ from collections import UserDict
3
+ import enum
4
+ import time
5
+
6
+
7
+ class TaskStatus(enum.Enum):
8
+ "These are the possible states a task can be in."
9
+ NOT_STARTED = enum.auto()
10
+ WAITING_FOR_DEPENDENCIES = enum.auto()
11
+ CANCELLED = enum.auto()
12
+ PARENT_FAILED = enum.auto()
13
+ WAITING_FOR_REQUEST_CAPACITY = enum.auto()
14
+ WAITING_FOR_TOKEN_CAPACITY = enum.auto()
15
+ API_CALL_IN_PROGRESS = enum.auto()
16
+ SUCCESS = enum.auto()
17
+ FAILED = enum.auto()
18
+
19
+
20
+ class TaskStatusLogEntry(UserDict):
21
+ def __init__(self, log_time, value):
22
+ self.data = {"log_time": log_time, "value": value}
23
+ super().__init__(self.data)
24
+
25
+
26
+ class TaskStatusDescriptor:
27
+ "The descriptor ensures that the task status is always an instance of the TaskStatus enum."
28
+
29
+ def __init__(self):
30
+ self._task_status = None
31
+
32
+ def __get__(self, instance, owner):
33
+ return self._task_status
34
+
35
+ def __set__(self, instance, value):
36
+ """Ensure that the value is an instance of TaskStatus."""
37
+ if not isinstance(value, TaskStatus):
38
+ raise ValueError("Value must be an instance of TaskStatus enum")
39
+ t = time.monotonic()
40
+ if hasattr(instance, "status_log"):
41
+ instance.status_log.append(TaskStatusLogEntry(t, value))
42
+ self._task_status = value
43
+
44
+ def __delete__(self, instance):
45
+ self._task_status = None
46
+
47
+
48
+ status_colors = {
49
+ TaskStatus.NOT_STARTED: "grey",
50
+ TaskStatus.WAITING_FOR_DEPENDENCIES: "orange",
51
+ TaskStatus.WAITING_FOR_REQUEST_CAPACITY: "yellow",
52
+ TaskStatus.WAITING_FOR_TOKEN_CAPACITY: "gold",
53
+ TaskStatus.CANCELLED: "white",
54
+ TaskStatus.PARENT_FAILED: "darkred",
55
+ TaskStatus.FAILED: "red",
56
+ TaskStatus.API_CALL_IN_PROGRESS: "blue",
57
+ TaskStatus.SUCCESS: "green",
58
+ }
59
+
60
+
61
+ def get_enum_from_string(str_key):
62
+ """Parse the string to extract the enum member name."""
63
+ try:
64
+ _, member_name = str_key.split(".")
65
+ enum_member = getattr(TaskStatus, member_name)
66
+ return enum_member
67
+ except ValueError:
68
+ return str_key
69
+
70
+
71
+ class InterviewTaskLogDict(UserDict):
72
+ """A dictionary of TaskStatusLog objects.
73
+
74
+ The key is the name of the task.
75
+ """
76
+
77
+ @property
78
+ def min_time(self):
79
+ return min([log.min_time for log in self.values()])
80
+
81
+ @property
82
+ def max_time(self):
83
+ return max([log.max_time for log in self.values()])
84
+
85
+ def status_matrix(self, num_periods):
86
+ """Return a matrix of status values."""
87
+ start_time = self.min_time
88
+ end_time = self.max_time
89
+ time_increment = (end_time - start_time) / num_periods
90
+ status_matrix = {}
91
+ time_periods = [start_time + i * time_increment for i in range(num_periods)]
92
+ for task_name, log in self.items():
93
+ status_matrix[task_name] = [log.status_at_time(t) for t in time_periods]
94
+ return status_matrix
95
+
96
+ def numerical_matrix(self, num_periods):
97
+ """Return a numerical matrix of status values."""
98
+ status_dicts = self.status_matrix(num_periods)
99
+
100
+ num_cols = num_periods
101
+ num_rows = len(status_dicts)
102
+ matrix = [[0 for _ in range(num_cols)] for _ in range(num_rows)]
103
+
104
+ for row_index, (task_name, status_list) in enumerate(status_dicts.items()):
105
+ matrix[row_index] = [
106
+ list(status_colors.keys()).index(status) for status in status_list
107
+ ]
108
+
109
+ index_to_names = {i: name for i, name in enumerate(status_dicts.keys())}
110
+ return matrix, index_to_names
111
+
112
+ def visualize(self, num_periods=10):
113
+ """Visualize the status matrix with outlined squares."""
114
+ import matplotlib.pyplot as plt
115
+ from matplotlib.colors import ListedColormap
116
+ import numpy as np
117
+ from matplotlib.patches import Rectangle
118
+
119
+ # Define your custom colormap
120
+ custom_cmap = ListedColormap(list(status_colors.values()))
121
+
122
+ # Generate the matrix
123
+ matrix, index_to_names = self.numerical_matrix(num_periods)
124
+
125
+ # Create the figure and axes
126
+ plt.figure(figsize=(10, 5))
127
+ ax = plt.gca()
128
+
129
+ # Display the matrix and keep a reference to the imshow object
130
+ im = ax.imshow(matrix, aspect="auto", cmap=custom_cmap)
131
+
132
+ # Adding color bar, now correctly associating it with 'im'
133
+ cbar = plt.colorbar(im, ticks=range(len(status_colors)), label="Task Status")
134
+
135
+ cbar_labels = [status.name for status in status_colors.keys()]
136
+ # breakpoint()
137
+ cbar.set_ticklabels(cbar_labels) # Setting the custom labels for the colorbar
138
+
139
+ im.set_clim(
140
+ -0.5, len(status_colors) - 0.5
141
+ ) # Setting color limits directly on the imshow object
142
+
143
+ # Outline each cell by drawing rectangles
144
+ for (j, i), val in np.ndenumerate(matrix):
145
+ ax.add_patch(
146
+ Rectangle(
147
+ (i - 0.5, j - 0.5), 1, 1, fill=False, edgecolor="black", lw=0.5
148
+ )
149
+ )
150
+
151
+ # Set custom y-axis ticks and labels
152
+ yticks = list(index_to_names.keys())
153
+ yticklabels = list(index_to_names.values())
154
+ plt.yticks(ticks=yticks, labels=yticklabels)
155
+
156
+ # Show the plot
157
+ plt.show()
158
+
159
+
160
+ if __name__ == "__main__":
161
+ pass
@@ -0,0 +1,27 @@
1
+ from edsl.jobs.tokens.TokenUsage import TokenUsage
2
+ from edsl.enums import TokenPricing
3
+
4
+
5
+ class InterviewTokenUsage:
6
+ def __init__(
7
+ self, new_token_usage: TokenUsage = None, cached_token_usage: TokenUsage = None
8
+ ):
9
+ self.new_token_usage = new_token_usage or TokenUsage(from_cache=False)
10
+ self.cached_token_usage = cached_token_usage or TokenUsage(from_cache=True)
11
+
12
+ def __add__(self, other):
13
+ if not isinstance(other, InterviewTokenUsage):
14
+ raise ValueError(f"Can't add {type(other)} to InterviewTokenSummary")
15
+ return InterviewTokenUsage(
16
+ new_token_usage=self.new_token_usage + other.new_token_usage,
17
+ cached_token_usage=self.cached_token_usage + other.cached_token_usage,
18
+ )
19
+
20
+ def __repr__(self):
21
+ return f"InterviewTokenUsage(new_token_usage={self.new_token_usage}, cached_token_usage={self.cached_token_usage})"
22
+
23
+ def cost(self, prices: TokenPricing):
24
+ return self.new_token_usage.cost(prices)
25
+
26
+ def saved(self, prices: TokenPricing):
27
+ return self.cached_token_usage.cost(prices)