edsl 0.1.46__py3-none-any.whl → 0.1.48__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.
- edsl/__init__.py +44 -39
- edsl/__version__.py +1 -1
- edsl/agents/__init__.py +4 -2
- edsl/agents/{Agent.py → agent.py} +442 -152
- edsl/agents/{AgentList.py → agent_list.py} +220 -162
- edsl/agents/descriptors.py +46 -7
- edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
- edsl/base/__init__.py +75 -0
- edsl/base/base_class.py +1303 -0
- edsl/base/data_transfer_models.py +114 -0
- edsl/base/enums.py +215 -0
- edsl/base.py +8 -0
- edsl/buckets/__init__.py +25 -0
- edsl/buckets/bucket_collection.py +324 -0
- edsl/buckets/model_buckets.py +206 -0
- edsl/buckets/token_bucket.py +502 -0
- edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
- edsl/buckets/token_bucket_client.py +509 -0
- edsl/caching/__init__.py +20 -0
- edsl/caching/cache.py +814 -0
- edsl/caching/cache_entry.py +427 -0
- edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
- edsl/caching/exceptions.py +24 -0
- edsl/caching/orm.py +30 -0
- edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
- edsl/caching/sql_dict.py +441 -0
- edsl/config/__init__.py +8 -0
- edsl/config/config_class.py +177 -0
- edsl/config.py +4 -176
- edsl/conversation/Conversation.py +7 -7
- edsl/conversation/car_buying.py +4 -4
- edsl/conversation/chips.py +6 -6
- edsl/coop/__init__.py +25 -2
- edsl/coop/coop.py +430 -113
- edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
- edsl/coop/exceptions.py +62 -0
- edsl/coop/price_fetcher.py +126 -0
- edsl/coop/utils.py +89 -24
- edsl/data_transfer_models.py +5 -72
- edsl/dataset/__init__.py +10 -0
- edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
- edsl/dataset/dataset_operations_mixin.py +1492 -0
- edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
- edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
- edsl/{results → dataset/display}/table_renderers.py +58 -2
- edsl/{results → dataset}/file_exports.py +4 -5
- edsl/{results → dataset}/smart_objects.py +2 -2
- edsl/enums.py +5 -205
- edsl/inference_services/__init__.py +5 -0
- edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
- edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
- edsl/inference_services/data_structures.py +3 -2
- edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
- edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
- edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
- edsl/inference_services/registry.py +4 -41
- edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
- edsl/inference_services/services/__init__.py +31 -0
- edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
- edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
- edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
- edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
- edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
- edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
- edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
- edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
- edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
- edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
- edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +12 -12
- edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
- edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
- edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
- edsl/inference_services/write_available.py +1 -2
- edsl/instructions/__init__.py +6 -0
- edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
- edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
- edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
- edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
- edsl/interviews/__init__.py +4 -0
- edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
- edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
- edsl/interviews/interview.py +638 -0
- edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
- edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
- edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
- edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
- edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
- edsl/invigilators/__init__.py +38 -0
- edsl/invigilators/invigilator_base.py +477 -0
- edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
- edsl/invigilators/prompt_constructor.py +476 -0
- edsl/{agents → invigilators}/prompt_helpers.py +2 -1
- edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
- edsl/{agents → invigilators}/question_option_processor.py +96 -21
- edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
- edsl/jobs/__init__.py +7 -1
- edsl/jobs/async_interview_runner.py +99 -35
- edsl/jobs/check_survey_scenario_compatibility.py +7 -5
- edsl/jobs/data_structures.py +153 -22
- edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
- edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
- edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
- edsl/jobs/{Jobs.py → jobs.py} +321 -155
- edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
- edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +20 -17
- edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
- edsl/jobs/jobs_pricing_estimation.py +347 -0
- edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
- edsl/jobs/jobs_runner_asyncio.py +282 -0
- edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/key_management/__init__.py +28 -0
- edsl/key_management/key_lookup.py +161 -0
- edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
- edsl/key_management/key_lookup_collection.py +82 -0
- edsl/key_management/models.py +218 -0
- edsl/language_models/__init__.py +7 -2
- edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
- edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
- edsl/language_models/language_model.py +1080 -0
- edsl/language_models/model.py +10 -25
- edsl/language_models/{ModelList.py → model_list.py} +9 -14
- edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
- edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
- edsl/language_models/repair.py +4 -4
- edsl/language_models/utilities.py +4 -4
- edsl/notebooks/__init__.py +3 -1
- edsl/notebooks/{Notebook.py → notebook.py} +7 -8
- edsl/prompts/__init__.py +1 -1
- edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
- edsl/prompts/{Prompt.py → prompt.py} +101 -95
- edsl/questions/HTMLQuestion.py +1 -1
- edsl/questions/__init__.py +154 -25
- edsl/questions/answer_validator_mixin.py +1 -1
- edsl/questions/compose_questions.py +4 -3
- edsl/questions/derived/question_likert_five.py +166 -0
- edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
- edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
- edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
- edsl/questions/descriptors.py +24 -30
- edsl/questions/loop_processor.py +65 -19
- edsl/questions/question_base.py +881 -0
- edsl/questions/question_base_gen_mixin.py +15 -16
- edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
- edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
- edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
- edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
- edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
- edsl/questions/question_free_text.py +282 -0
- edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
- edsl/questions/{QuestionList.py → question_list.py} +6 -7
- edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
- edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
- edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
- edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
- edsl/questions/question_registry.py +10 -16
- edsl/questions/register_questions_meta.py +8 -4
- edsl/questions/response_validator_abc.py +17 -16
- edsl/results/__init__.py +4 -1
- edsl/{exceptions/results.py → results/exceptions.py} +1 -1
- edsl/results/report.py +197 -0
- edsl/results/{Result.py → result.py} +131 -45
- edsl/results/{Results.py → results.py} +420 -216
- edsl/results/results_selector.py +344 -25
- edsl/scenarios/__init__.py +30 -3
- edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
- edsl/scenarios/directory_scanner.py +156 -13
- edsl/scenarios/document_chunker.py +186 -0
- edsl/scenarios/exceptions.py +101 -0
- edsl/scenarios/file_methods.py +2 -3
- edsl/scenarios/file_store.py +755 -0
- edsl/scenarios/handlers/__init__.py +14 -14
- edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
- edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
- edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
- edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
- edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
- edsl/scenarios/handlers/latex_file_store.py +5 -0
- edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
- edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
- edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
- edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
- edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
- edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
- edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
- edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
- edsl/scenarios/scenario.py +928 -0
- edsl/scenarios/scenario_join.py +18 -5
- edsl/scenarios/{ScenarioList.py → scenario_list.py} +424 -106
- edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
- edsl/scenarios/scenario_selector.py +5 -1
- edsl/study/ObjectEntry.py +2 -2
- edsl/study/SnapShot.py +5 -5
- edsl/study/Study.py +20 -21
- edsl/study/__init__.py +6 -4
- edsl/surveys/__init__.py +7 -4
- edsl/surveys/dag/__init__.py +2 -0
- edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
- edsl/surveys/{DAG.py → dag/dag.py} +13 -10
- edsl/surveys/descriptors.py +1 -1
- edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
- edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
- edsl/surveys/memory/__init__.py +3 -0
- edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
- edsl/surveys/rules/__init__.py +3 -0
- edsl/surveys/{Rule.py → rules/rule.py} +103 -43
- edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
- edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
- edsl/surveys/survey.py +1743 -0
- edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
- edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
- edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
- edsl/tasks/__init__.py +32 -0
- edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
- edsl/tasks/task_creators.py +135 -0
- edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
- edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
- edsl/tasks/task_status_log.py +85 -0
- edsl/tokens/__init__.py +2 -0
- edsl/tokens/interview_token_usage.py +53 -0
- edsl/utilities/PrettyList.py +1 -1
- edsl/utilities/SystemInfo.py +25 -22
- edsl/utilities/__init__.py +29 -21
- edsl/utilities/gcp_bucket/__init__.py +2 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
- edsl/utilities/interface.py +44 -536
- edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
- edsl/utilities/repair_functions.py +1 -1
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/METADATA +3 -2
- edsl-0.1.48.dist-info/RECORD +347 -0
- edsl/Base.py +0 -426
- edsl/BaseDiff.py +0 -260
- edsl/agents/InvigilatorBase.py +0 -260
- edsl/agents/PromptConstructor.py +0 -318
- edsl/auto/AutoStudy.py +0 -130
- edsl/auto/StageBase.py +0 -243
- edsl/auto/StageGenerateSurvey.py +0 -178
- edsl/auto/StageLabelQuestions.py +0 -125
- edsl/auto/StagePersona.py +0 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +0 -88
- edsl/auto/StagePersonaDimensionValues.py +0 -74
- edsl/auto/StagePersonaDimensions.py +0 -69
- edsl/auto/StageQuestions.py +0 -74
- edsl/auto/SurveyCreatorPipeline.py +0 -21
- edsl/auto/utilities.py +0 -218
- edsl/base/Base.py +0 -279
- edsl/coop/PriceFetcher.py +0 -54
- edsl/data/Cache.py +0 -580
- edsl/data/CacheEntry.py +0 -230
- edsl/data/SQLiteDict.py +0 -292
- edsl/data/__init__.py +0 -5
- edsl/data/orm.py +0 -10
- edsl/exceptions/cache.py +0 -5
- edsl/exceptions/coop.py +0 -14
- edsl/exceptions/data.py +0 -14
- edsl/exceptions/scenarios.py +0 -29
- edsl/jobs/Answers.py +0 -43
- edsl/jobs/JobsPrompts.py +0 -354
- edsl/jobs/buckets/BucketCollection.py +0 -134
- edsl/jobs/buckets/ModelBuckets.py +0 -65
- edsl/jobs/buckets/TokenBucket.py +0 -283
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/interviews/Interview.py +0 -395
- edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
- edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
- edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
- edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
- edsl/jobs/tasks/TaskCreators.py +0 -64
- edsl/jobs/tasks/TaskStatusLog.py +0 -23
- edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
- edsl/language_models/LanguageModel.py +0 -635
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/models.py +0 -137
- edsl/questions/QuestionBase.py +0 -539
- edsl/questions/QuestionFreeText.py +0 -130
- edsl/questions/derived/QuestionLikertFive.py +0 -76
- edsl/results/DatasetExportMixin.py +0 -911
- edsl/results/ResultsExportMixin.py +0 -45
- edsl/results/TextEditor.py +0 -50
- edsl/results/results_fetch_mixin.py +0 -33
- edsl/results/results_tools_mixin.py +0 -98
- edsl/scenarios/DocumentChunker.py +0 -104
- edsl/scenarios/FileStore.py +0 -564
- edsl/scenarios/Scenario.py +0 -548
- edsl/scenarios/ScenarioHtmlMixin.py +0 -65
- edsl/scenarios/ScenarioListExportMixin.py +0 -45
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/shared.py +0 -1
- edsl/surveys/Survey.py +0 -1306
- edsl/surveys/SurveyQualtricsImport.py +0 -284
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/tools/__init__.py +0 -1
- edsl/tools/clusters.py +0 -192
- edsl/tools/embeddings.py +0 -27
- edsl/tools/embeddings_plotting.py +0 -118
- edsl/tools/plotting.py +0 -112
- edsl/tools/summarize.py +0 -18
- edsl/utilities/data/Registry.py +0 -6
- edsl/utilities/data/__init__.py +0 -1
- edsl/utilities/data/scooter_results.json +0 -1
- edsl-0.1.46.dist-info/RECORD +0 -366
- /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
- /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
- /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
- /edsl/{results → dataset/display}/table_data_class.py +0 -0
- /edsl/{results → dataset/display}/table_display.css +0 -0
- /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
- /edsl/{results → dataset}/tree_explore.py +0 -0
- /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
- /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
- /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
- /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
- /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
- /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
- /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
- /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
- /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
- /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
- /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
- /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
- /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
- /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
- /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
- {edsl-0.1.46.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -1,11 +1,47 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
This module provides the TaskHistory class for tracking and analyzing task execution history.
|
3
|
+
|
4
|
+
The TaskHistory class maintains a record of all interviews conducted by EDSL, including
|
5
|
+
their task execution histories, exceptions, and performance metrics. It supports rich
|
6
|
+
visualization and reporting to help users understand task execution patterns and diagnose
|
7
|
+
issues.
|
8
|
+
"""
|
9
|
+
|
10
|
+
from typing import List, Optional, Dict, Any, Union
|
2
11
|
from io import BytesIO
|
3
12
|
import base64
|
4
|
-
|
5
|
-
|
13
|
+
import os
|
14
|
+
import tempfile
|
15
|
+
|
16
|
+
from .task_status_enum import TaskStatus
|
17
|
+
from ..base import RepresentationMixin
|
6
18
|
|
7
19
|
|
8
20
|
class TaskHistory(RepresentationMixin):
|
21
|
+
"""
|
22
|
+
Records and analyzes the execution history of tasks across multiple interviews.
|
23
|
+
|
24
|
+
The TaskHistory class serves as a central repository for tracking task execution
|
25
|
+
across multiple interviews. It provides methods for:
|
26
|
+
|
27
|
+
1. Error Analysis - Collecting, categorizing, and reporting exceptions
|
28
|
+
2. Execution Visualization - Generating plots of task status over time
|
29
|
+
3. Performance Metrics - Calculating timing statistics for tasks
|
30
|
+
4. HTML Reports - Creating detailed interactive reports of execution
|
31
|
+
|
32
|
+
This class is particularly useful for debugging complex interview workflows,
|
33
|
+
identifying performance bottlenecks, and understanding patterns in task execution.
|
34
|
+
It supports both interactive exploration in notebooks and standalone report
|
35
|
+
generation.
|
36
|
+
|
37
|
+
Key features:
|
38
|
+
- Tracks exceptions with optional traceback storage
|
39
|
+
- Provides visualizations of task status transitions
|
40
|
+
- Generates interactive HTML reports with filtering and drill-down
|
41
|
+
- Computes statistics across interviews (by model, question type, etc.)
|
42
|
+
- Exports to various formats (HTML, notebook, etc.)
|
43
|
+
"""
|
44
|
+
|
9
45
|
def __init__(
|
10
46
|
self,
|
11
47
|
interviews: List["Interview"] = None,
|
@@ -14,12 +50,16 @@ class TaskHistory(RepresentationMixin):
|
|
14
50
|
interviews_with_exceptions_only: bool = False,
|
15
51
|
):
|
16
52
|
"""
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
53
|
+
Initialize a TaskHistory to track execution across multiple interviews.
|
54
|
+
|
55
|
+
Parameters:
|
56
|
+
interviews: List of Interview objects to track
|
57
|
+
include_traceback: Whether to include full exception tracebacks
|
58
|
+
max_interviews: Maximum number of interviews to display in reports
|
59
|
+
interviews_with_exceptions_only: If True, only track interviews with exceptions
|
60
|
+
|
61
|
+
Example:
|
62
|
+
>>> _ = TaskHistory.example() # Create a sample TaskHistory
|
23
63
|
"""
|
24
64
|
self.interviews_with_exceptions_only = interviews_with_exceptions_only
|
25
65
|
self._interviews = {}
|
@@ -34,10 +74,8 @@ class TaskHistory(RepresentationMixin):
|
|
34
74
|
}
|
35
75
|
self.max_interviews = max_interviews
|
36
76
|
|
37
|
-
# self.total_interviews = interviews
|
38
77
|
self.include_traceback = include_traceback
|
39
78
|
|
40
|
-
# self._interviews = {index: i for index, i in enumerate(self.total_interviews)}
|
41
79
|
self.max_interviews = max_interviews
|
42
80
|
|
43
81
|
def add_interview(self, interview: "Interview"):
|
@@ -51,9 +89,8 @@ class TaskHistory(RepresentationMixin):
|
|
51
89
|
@classmethod
|
52
90
|
def example(cls):
|
53
91
|
""" """
|
54
|
-
from
|
55
|
-
|
56
|
-
from edsl.jobs.Jobs import Jobs
|
92
|
+
from ..interviews import Interview
|
93
|
+
from ..jobs import Jobs
|
57
94
|
|
58
95
|
j = Jobs.example(throw_exception_probability=1, test_model=True)
|
59
96
|
|
@@ -120,7 +157,7 @@ class TaskHistory(RepresentationMixin):
|
|
120
157
|
if data is None:
|
121
158
|
return cls([], include_traceback=False)
|
122
159
|
|
123
|
-
from
|
160
|
+
from ..interviews import Interview
|
124
161
|
|
125
162
|
interviews = [Interview.from_dict(i) for i in data["interviews"]]
|
126
163
|
return cls(interviews, include_traceback=data["include_traceback"])
|
@@ -273,15 +310,15 @@ class TaskHistory(RepresentationMixin):
|
|
273
310
|
for exception in exceptions:
|
274
311
|
key = (
|
275
312
|
exception.exception.__class__.__name__, # Exception type
|
276
|
-
interview.model._inference_service_,
|
277
|
-
interview.model.model,
|
278
|
-
question_name
|
313
|
+
interview.model._inference_service_, # Service
|
314
|
+
interview.model.model, # Model
|
315
|
+
question_name, # Question name
|
279
316
|
)
|
280
317
|
if key not in exceptions_table:
|
281
318
|
exceptions_table[key] = 0
|
282
319
|
exceptions_table[key] += 1
|
283
320
|
return exceptions_table
|
284
|
-
|
321
|
+
|
285
322
|
@property
|
286
323
|
def exceptions_by_type(self) -> dict:
|
287
324
|
"""Return a dictionary of exceptions tallied by type."""
|
@@ -342,27 +379,6 @@ class TaskHistory(RepresentationMixin):
|
|
342
379
|
}
|
343
380
|
return sorted_exceptions_by_question_name
|
344
381
|
|
345
|
-
# @property
|
346
|
-
# def exceptions_by_model(self) -> dict:
|
347
|
-
# """Return a dictionary of exceptions tallied by model and question name."""
|
348
|
-
# exceptions_by_model = {}
|
349
|
-
# for interview in self.total_interviews:
|
350
|
-
# model = interview.model.model
|
351
|
-
# service = interview.model._inference_service_
|
352
|
-
# if (service, model) not in exceptions_by_model:
|
353
|
-
# exceptions_by_model[(service, model)] = 0
|
354
|
-
# if interview.exceptions != {}:
|
355
|
-
# exceptions_by_model[(service, model)] += len(interview.exceptions)
|
356
|
-
|
357
|
-
# # sort the exceptions by model
|
358
|
-
# sorted_exceptions_by_model = {
|
359
|
-
# k: v
|
360
|
-
# for k, v in sorted(
|
361
|
-
# exceptions_by_model.items(), key=lambda item: item[1], reverse=True
|
362
|
-
# )
|
363
|
-
# }
|
364
|
-
# return sorted_exceptions_by_model
|
365
|
-
|
366
382
|
@property
|
367
383
|
def exceptions_by_model(self) -> dict:
|
368
384
|
"""Return a dictionary of exceptions tallied by model and question name."""
|
@@ -389,7 +405,7 @@ class TaskHistory(RepresentationMixin):
|
|
389
405
|
models_used = set([i.model.model for index, i in self._interviews.items()])
|
390
406
|
|
391
407
|
from jinja2 import Environment, FileSystemLoader
|
392
|
-
from edsl.
|
408
|
+
from edsl.utilities import TemplateLoader
|
393
409
|
|
394
410
|
env = Environment(loader=TemplateLoader("edsl", "templates/error_reporting"))
|
395
411
|
|
@@ -417,12 +433,35 @@ class TaskHistory(RepresentationMixin):
|
|
417
433
|
def html(
|
418
434
|
self,
|
419
435
|
filename: Optional[str] = None,
|
420
|
-
return_link=False,
|
421
|
-
css=None,
|
422
|
-
cta="<br><span style='font-size: 18px; font-weight: medium-bold; text-decoration: underline;'>Click to open the report in a new tab</span><br><br>",
|
423
|
-
open_in_browser=False,
|
424
|
-
):
|
425
|
-
"""
|
436
|
+
return_link: bool = False,
|
437
|
+
css: Optional[str] = None,
|
438
|
+
cta: str = "<br><span style='font-size: 18px; font-weight: medium-bold; text-decoration: underline;'>Click to open the report in a new tab</span><br><br>",
|
439
|
+
open_in_browser: bool = False,
|
440
|
+
) -> Optional[str]:
|
441
|
+
"""
|
442
|
+
Generate and display an interactive HTML report of task execution.
|
443
|
+
|
444
|
+
This method creates a comprehensive HTML report showing task execution details,
|
445
|
+
exceptions, timing information, and statistics across all tracked interviews.
|
446
|
+
In notebook environments, it displays an embedded preview with a link to open
|
447
|
+
the full report in a new tab.
|
448
|
+
|
449
|
+
Parameters:
|
450
|
+
filename: Path to save the HTML report (if None, a temporary file is created)
|
451
|
+
return_link: If True, return the path to the saved HTML file
|
452
|
+
css: Custom CSS to apply to the report (if None, uses default styling)
|
453
|
+
cta: HTML for the "Call to Action" link text
|
454
|
+
open_in_browser: If True, automatically open the report in the default browser
|
455
|
+
|
456
|
+
Returns:
|
457
|
+
If return_link is True, returns the path to the saved HTML file; otherwise None
|
458
|
+
|
459
|
+
Notes:
|
460
|
+
- In Jupyter notebooks, displays an embedded preview with a link
|
461
|
+
- In terminal environments, saves the file and prints its location
|
462
|
+
- The report includes interactive elements for filtering and drill-down
|
463
|
+
- Exception details, status transitions, and timing are all included
|
464
|
+
"""
|
426
465
|
from IPython.display import display, HTML
|
427
466
|
import tempfile
|
428
467
|
import os
|
@@ -5,7 +5,27 @@ import time
|
|
5
5
|
|
6
6
|
|
7
7
|
class TaskStatus(enum.Enum):
|
8
|
-
"
|
8
|
+
"""
|
9
|
+
Enumeration of possible states for a task in the EDSL task system.
|
10
|
+
|
11
|
+
Each task moves through various states during its lifecycle, from creation
|
12
|
+
to completion. This enum defines all possible states to track task progress
|
13
|
+
and diagnose issues.
|
14
|
+
|
15
|
+
States:
|
16
|
+
NOT_STARTED: Initial state - task has been created but not yet started
|
17
|
+
WAITING_FOR_DEPENDENCIES: Task is waiting for prerequisite tasks to complete
|
18
|
+
CANCELLED: Task was explicitly cancelled by the user or system
|
19
|
+
PARENT_FAILED: Task cannot run because a dependency task failed
|
20
|
+
WAITING_FOR_REQUEST_CAPACITY: Task is waiting due to API rate limits
|
21
|
+
WAITING_FOR_TOKEN_CAPACITY: Task is waiting due to token usage limits
|
22
|
+
API_CALL_IN_PROGRESS: Task is actively executing an API call
|
23
|
+
SUCCESS: Task completed successfully
|
24
|
+
FAILED: Task encountered an error and failed to complete
|
25
|
+
|
26
|
+
These states are used throughout EDSL to track task progress, generate
|
27
|
+
visualizations, and provide detailed error reports.
|
28
|
+
"""
|
9
29
|
NOT_STARTED = enum.auto()
|
10
30
|
WAITING_FOR_DEPENDENCIES = enum.auto()
|
11
31
|
CANCELLED = enum.auto()
|
@@ -18,30 +38,80 @@ class TaskStatus(enum.Enum):
|
|
18
38
|
|
19
39
|
|
20
40
|
class TaskStatusLogEntry(UserDict):
|
21
|
-
|
41
|
+
"""
|
42
|
+
A timestamped record of a task's status change.
|
43
|
+
|
44
|
+
This class records both the time when a task's status changed and the new status value.
|
45
|
+
It uses the UserDict interface for convenient dictionary-like access while maintaining
|
46
|
+
the structured nature of status log entries.
|
47
|
+
|
48
|
+
Attributes:
|
49
|
+
log_time: The time (from time.monotonic()) when the status change occurred
|
50
|
+
value: The new TaskStatus value
|
51
|
+
"""
|
52
|
+
def __init__(self, log_time: float, value: TaskStatus):
|
53
|
+
"""
|
54
|
+
Create a new task status log entry.
|
55
|
+
|
56
|
+
Parameters:
|
57
|
+
log_time: The time when this status change occurred (from time.monotonic())
|
58
|
+
value: The TaskStatus value that the task transitioned to
|
59
|
+
"""
|
22
60
|
self.data = {"log_time": log_time, "value": value}
|
23
61
|
super().__init__(self.data)
|
24
62
|
|
25
63
|
|
26
64
|
class TaskStatusDescriptor:
|
27
|
-
"
|
65
|
+
"""
|
66
|
+
A descriptor that enforces TaskStatus type safety and logs status changes.
|
67
|
+
|
68
|
+
This descriptor is used to create task_status properties in task-related classes.
|
69
|
+
It performs two key functions:
|
70
|
+
|
71
|
+
1. Type Enforcement: Ensures that task_status is always set to a valid TaskStatus enum
|
72
|
+
2. Logging: Automatically adds entries to the task's status_log when status changes
|
73
|
+
|
74
|
+
By using this descriptor, EDSL ensures consistent status tracking across all tasks
|
75
|
+
while providing a rich history of status changes for debugging and visualization.
|
76
|
+
"""
|
28
77
|
|
29
78
|
def __init__(self):
|
79
|
+
"""Initialize the descriptor with a null status value."""
|
30
80
|
self._task_status = None
|
31
81
|
|
32
82
|
def __get__(self, instance, owner):
|
83
|
+
"""Return the current task status."""
|
33
84
|
return self._task_status
|
34
85
|
|
35
86
|
def __set__(self, instance, value):
|
36
|
-
"""
|
87
|
+
"""
|
88
|
+
Set a new task status and record the change in the status log.
|
89
|
+
|
90
|
+
This method enforces that the value is a valid TaskStatus enum and
|
91
|
+
automatically adds an entry to the instance's status_log (if it exists).
|
92
|
+
|
93
|
+
Parameters:
|
94
|
+
instance: The object instance that owns this descriptor
|
95
|
+
value: The new TaskStatus value to set
|
96
|
+
|
97
|
+
Raises:
|
98
|
+
ValueError: If value is not an instance of TaskStatus enum
|
99
|
+
"""
|
37
100
|
if not isinstance(value, TaskStatus):
|
38
101
|
raise ValueError("Value must be an instance of TaskStatus enum")
|
102
|
+
|
103
|
+
# Record the current time for the status change
|
39
104
|
t = time.monotonic()
|
105
|
+
|
106
|
+
# Add an entry to the status log if the instance has one
|
40
107
|
if hasattr(instance, "status_log"):
|
41
108
|
instance.status_log.append(TaskStatusLogEntry(t, value))
|
109
|
+
|
110
|
+
# Update the actual status value
|
42
111
|
self._task_status = value
|
43
112
|
|
44
113
|
def __delete__(self, instance):
|
114
|
+
"""Reset the task status to None when deleted."""
|
45
115
|
self._task_status = None
|
46
116
|
|
47
117
|
|
@@ -69,9 +139,23 @@ def get_enum_from_string(str_key):
|
|
69
139
|
|
70
140
|
|
71
141
|
class InterviewTaskLogDict(UserDict):
|
72
|
-
"""
|
73
|
-
|
74
|
-
|
142
|
+
"""
|
143
|
+
A collection of task status logs for all tasks in an interview.
|
144
|
+
|
145
|
+
This dictionary-like object maps task names to their individual TaskStatusLog objects,
|
146
|
+
providing methods to analyze task execution across an entire interview. It supports
|
147
|
+
calculating timing metrics, generating status matrices for visualization, and
|
148
|
+
rendering graphical representations of task execution flow.
|
149
|
+
|
150
|
+
The InterviewTaskLogDict is a key component in EDSL's task monitoring system,
|
151
|
+
enabling both debugging of individual interviews and aggregate analysis of
|
152
|
+
execution patterns.
|
153
|
+
|
154
|
+
Key features:
|
155
|
+
- Temporal analysis (min/max execution times)
|
156
|
+
- Status matrix generation for visualization
|
157
|
+
- Visual representation of task status changes over time
|
158
|
+
- Color-coded status visualization
|
75
159
|
"""
|
76
160
|
|
77
161
|
@property
|
@@ -0,0 +1,85 @@
|
|
1
|
+
"""
|
2
|
+
This module provides the TaskStatusLog class for tracking the status history of tasks.
|
3
|
+
|
4
|
+
The TaskStatusLog class maintains an ordered list of status changes for a specific task,
|
5
|
+
with timestamps and status values. This history allows for detailed analysis of task
|
6
|
+
execution, including timing, state transitions, and status at any point in time.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from collections import UserList
|
10
|
+
from typing import List, Optional, Union
|
11
|
+
|
12
|
+
from .task_status_enum import TaskStatus, TaskStatusLogEntry
|
13
|
+
|
14
|
+
|
15
|
+
class TaskStatusLog(UserList):
|
16
|
+
"""
|
17
|
+
An ordered history of status changes for a single task.
|
18
|
+
|
19
|
+
This class extends UserList to provide a chronological record of all status changes
|
20
|
+
that a task undergoes during its lifecycle. Each entry in the list is a
|
21
|
+
TaskStatusLogEntry object containing a timestamp and status value.
|
22
|
+
|
23
|
+
The TaskStatusLog provides methods to analyze the timing of task execution and
|
24
|
+
determine task status at any point in time. This information is valuable for
|
25
|
+
debugging, performance analysis, and visualization of task execution flow.
|
26
|
+
|
27
|
+
Key features:
|
28
|
+
- Records all status transitions with timestamps
|
29
|
+
- Provides min/max execution time calculations
|
30
|
+
- Supports interpolation to determine status at any given time
|
31
|
+
- Used by visualization tools to render task execution timelines
|
32
|
+
"""
|
33
|
+
|
34
|
+
@property
|
35
|
+
def min_time(self) -> float:
|
36
|
+
"""
|
37
|
+
Get the timestamp of the first status change.
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
The timestamp (from time.monotonic()) of the earliest status entry
|
41
|
+
|
42
|
+
Note:
|
43
|
+
This is typically when the task was first created and set to NOT_STARTED
|
44
|
+
"""
|
45
|
+
return self[0]["log_time"]
|
46
|
+
|
47
|
+
@property
|
48
|
+
def max_time(self) -> float:
|
49
|
+
"""
|
50
|
+
Get the timestamp of the last status change.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
The timestamp (from time.monotonic()) of the most recent status entry
|
54
|
+
|
55
|
+
Note:
|
56
|
+
This is typically when the task reached its final state (SUCCESS, FAILED, etc.)
|
57
|
+
"""
|
58
|
+
return self[-1]["log_time"]
|
59
|
+
|
60
|
+
def status_at_time(self, t: float) -> TaskStatus:
|
61
|
+
"""
|
62
|
+
Determine what status the task had at a specific point in time.
|
63
|
+
|
64
|
+
This method interpolates between status log entries to determine the task's
|
65
|
+
status at any arbitrary time point. It searches for the first status change
|
66
|
+
that occurred after time t and returns the status that was active at that time.
|
67
|
+
|
68
|
+
Parameters:
|
69
|
+
t: The timestamp to query (from time.monotonic())
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
The TaskStatus that was active at time t
|
73
|
+
|
74
|
+
Note:
|
75
|
+
If t is after the last recorded status change, the final status is returned.
|
76
|
+
If t is before the first recorded status change, this method may not behave
|
77
|
+
as expected since it assumes ordered traversal through the log.
|
78
|
+
|
79
|
+
TODO:
|
80
|
+
Could re-factor with bisect to make this faster for large logs.
|
81
|
+
"""
|
82
|
+
for entry in self:
|
83
|
+
if entry["log_time"] > t:
|
84
|
+
return entry["value"]
|
85
|
+
return self[-1]["value"]
|
edsl/tokens/__init__.py
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from .token_usage import TokenUsage
|
4
|
+
from edsl.enums import TokenPricing
|
5
|
+
|
6
|
+
class InterviewTokenUsage:
|
7
|
+
"""A class to represent the token usage of an interview."""
|
8
|
+
|
9
|
+
def __init__(
|
10
|
+
self, new_token_usage: Optional[TokenUsage] = None, cached_token_usage: Optional[TokenUsage] = None
|
11
|
+
):
|
12
|
+
"""Initialize the InterviewTokenUsage.
|
13
|
+
|
14
|
+
>>> usage = InterviewTokenUsage()
|
15
|
+
"""
|
16
|
+
self.new_token_usage = new_token_usage or TokenUsage(from_cache=False)
|
17
|
+
self.cached_token_usage = cached_token_usage or TokenUsage(from_cache=True)
|
18
|
+
|
19
|
+
def __add__(self, other: "InterviewTokenUsage") -> "InterviewTokenUsage":
|
20
|
+
"""Add two InterviewTokenUsage objects together.
|
21
|
+
|
22
|
+
>>> usage1 = InterviewTokenUsage()
|
23
|
+
>>> usage2 = InterviewTokenUsage()
|
24
|
+
>>> usage3 = usage1 + usage2
|
25
|
+
"""
|
26
|
+
if not isinstance(other, InterviewTokenUsage):
|
27
|
+
raise ValueError(f"Can't add {type(other)} to InterviewTokenSummary")
|
28
|
+
return InterviewTokenUsage(
|
29
|
+
new_token_usage=self.new_token_usage + other.new_token_usage,
|
30
|
+
cached_token_usage=self.cached_token_usage + other.cached_token_usage,
|
31
|
+
)
|
32
|
+
|
33
|
+
def __repr__(self):
|
34
|
+
return f"InterviewTokenUsage(new_token_usage={self.new_token_usage}, cached_token_usage={self.cached_token_usage})"
|
35
|
+
|
36
|
+
def cost(self, prices: TokenPricing) -> float:
|
37
|
+
"""Return the cost of the new and cached token usage.
|
38
|
+
|
39
|
+
>>> usage = InterviewTokenUsage()
|
40
|
+
>>> usage.cost(TokenPricing.example())
|
41
|
+
0.0
|
42
|
+
"""
|
43
|
+
return self.new_token_usage.cost(prices)
|
44
|
+
|
45
|
+
def saved(self, prices: TokenPricing) -> float:
|
46
|
+
"""Return the saved cost of the cached token usage.
|
47
|
+
"""
|
48
|
+
return self.cached_token_usage.cost(prices)
|
49
|
+
|
50
|
+
|
51
|
+
if __name__ == "__main__":
|
52
|
+
import doctest
|
53
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/utilities/PrettyList.py
CHANGED
edsl/utilities/SystemInfo.py
CHANGED
@@ -1,28 +1,31 @@
|
|
1
1
|
"""Module to store system information."""
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
import platform
|
6
|
-
import pkg_resources
|
3
|
+
# This module is not currently used by any part of the codebase.
|
4
|
+
# Keeping it commented out for potential future use.
|
7
5
|
|
6
|
+
# from dataclasses import dataclass
|
7
|
+
# import getpass
|
8
|
+
# import platform
|
9
|
+
# import pkg_resources
|
8
10
|
|
9
|
-
@dataclass
|
10
|
-
class SystemInfo:
|
11
|
-
"""Dataclass to store system information."""
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
package_name: str
|
17
|
-
package_version: str
|
12
|
+
# @dataclass
|
13
|
+
# class SystemInfo:
|
14
|
+
# """Dataclass to store system information."""
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
16
|
+
# username: str
|
17
|
+
# system_info: str
|
18
|
+
# release_info: str
|
19
|
+
# package_name: str
|
20
|
+
# package_version: str
|
21
|
+
|
22
|
+
# def __init__(self, package_name: str):
|
23
|
+
# """Initialize the dataclass with system."""
|
24
|
+
# self.username = getpass.getuser()
|
25
|
+
# self.system_info = platform.system()
|
26
|
+
# self.release_info = platform.release()
|
27
|
+
# self.package_name = package_name
|
28
|
+
# try:
|
29
|
+
# self.package_version = pkg_resources.get_distribution(package_name).version
|
30
|
+
# except pkg_resources.DistributionNotFound:
|
31
|
+
# self.package_version = "Not installed"
|
edsl/utilities/__init__.py
CHANGED
@@ -1,22 +1,30 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
1
|
+
# Core utilities - used across the codebase
|
2
|
+
from .template_loader import TemplateLoader
|
3
|
+
from .PrettyList import PrettyList
|
4
|
+
from .restricted_python import create_restricted_function
|
5
|
+
from .remove_edsl_version import remove_edsl_version
|
6
|
+
from .ast_utilities import extract_variable_names
|
7
|
+
|
8
|
+
# Functions from utilities.py
|
9
|
+
from .utilities import (
|
10
|
+
clean_json,
|
11
|
+
dict_hash,
|
12
|
+
hash_value,
|
13
|
+
repair_json,
|
14
|
+
create_valid_var_name,
|
15
|
+
random_string,
|
16
|
+
shorten_string,
|
17
|
+
is_gzipped
|
18
|
+
)
|
19
|
+
|
20
|
+
# Decorator utilities
|
21
|
+
from .decorators import sync_wrapper, jupyter_nb_handler
|
22
|
+
|
23
|
+
# Standalone utilities
|
24
|
+
from .is_notebook import is_notebook
|
25
|
+
from .is_valid_variable_name import is_valid_variable_name
|
26
|
+
from .naming_utilities import sanitize_string
|
27
|
+
|
28
|
+
# Interface module - note: print_results_long is imported directly in results.py
|
29
|
+
from .interface import print_results_long
|
9
30
|
|
10
|
-
# from edsl.utilities.utilities import (
|
11
|
-
# create_valid_var_name,
|
12
|
-
# dict_to_html,
|
13
|
-
# hash_value,
|
14
|
-
# HTMLSnippet,
|
15
|
-
# is_notebook,
|
16
|
-
# is_gzipped,
|
17
|
-
# is_valid_variable_name,
|
18
|
-
# random_string,
|
19
|
-
# repair_json,
|
20
|
-
# shorten_string,
|
21
|
-
# time_all_functions,
|
22
|
-
# )
|