edsl 0.1.39.dev1__py3-none-any.whl → 0.1.39.dev2__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/Base.py +169 -116
- edsl/__init__.py +14 -6
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +358 -146
- edsl/agents/AgentList.py +211 -73
- edsl/agents/Invigilator.py +88 -36
- edsl/agents/InvigilatorBase.py +59 -70
- edsl/agents/PromptConstructor.py +117 -219
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionOptionProcessor.py +172 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +0 -1
- edsl/agents/prompt_helpers.py +3 -3
- edsl/config.py +22 -2
- edsl/conversation/car_buying.py +2 -1
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +104 -42
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +21 -14
- edsl/data/CacheEntry.py +12 -15
- edsl/data/CacheHandler.py +33 -12
- edsl/data/__init__.py +4 -3
- edsl/data_transfer_models.py +2 -1
- edsl/enums.py +20 -0
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +12 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/questions.py +24 -6
- edsl/exceptions/scenarios.py +7 -0
- edsl/inference_services/AnthropicService.py +0 -3
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +209 -0
- edsl/inference_services/AwsBedrock.py +0 -2
- edsl/inference_services/AzureAI.py +0 -2
- edsl/inference_services/GoogleService.py +2 -11
- edsl/inference_services/InferenceServiceABC.py +18 -85
- edsl/inference_services/InferenceServicesCollection.py +105 -80
- edsl/inference_services/MistralAIService.py +0 -3
- edsl/inference_services/OpenAIService.py +1 -4
- edsl/inference_services/PerplexityService.py +0 -3
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +11 -8
- edsl/inference_services/data_structures.py +62 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +188 -0
- edsl/jobs/Answers.py +1 -14
- edsl/jobs/FetchInvigilator.py +40 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +48 -0
- edsl/jobs/Jobs.py +102 -243
- edsl/jobs/JobsChecks.py +35 -10
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +5 -3
- edsl/jobs/JobsRemoteInferenceHandler.py +128 -80
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/buckets/BucketCollection.py +44 -3
- edsl/jobs/buckets/TokenBucket.py +53 -21
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +77 -380
- edsl/jobs/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +4 -49
- edsl/jobs/tasks/QuestionTaskCreator.py +21 -19
- edsl/jobs/tasks/TaskHistory.py +14 -15
- edsl/jobs/tasks/task_status_enum.py +0 -2
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +137 -234
- edsl/language_models/ModelList.py +11 -13
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +0 -1
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/registry.py +49 -59
- edsl/language_models/repair.py +2 -2
- edsl/language_models/utilities.py +5 -4
- edsl/notebooks/Notebook.py +19 -14
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/prompts/Prompt.py +29 -39
- edsl/questions/AnswerValidatorMixin.py +47 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/LoopProcessor.py +149 -0
- edsl/questions/QuestionBase.py +37 -192
- edsl/questions/QuestionBaseGenMixin.py +52 -48
- edsl/questions/QuestionBasePromptsMixin.py +7 -3
- edsl/questions/QuestionCheckBox.py +1 -1
- edsl/questions/QuestionExtract.py +1 -1
- edsl/questions/QuestionFreeText.py +1 -2
- edsl/questions/QuestionList.py +3 -5
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +66 -22
- edsl/questions/QuestionNumerical.py +1 -3
- edsl/questions/QuestionRank.py +6 -16
- edsl/questions/ResponseValidatorABC.py +37 -11
- edsl/questions/ResponseValidatorFactory.py +28 -0
- edsl/questions/SimpleAskMixin.py +4 -3
- edsl/questions/__init__.py +1 -0
- edsl/questions/derived/QuestionLinearScale.py +6 -3
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +17 -3
- edsl/questions/question_registry.py +1 -1
- edsl/questions/templates/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- edsl/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +170 -7
- edsl/results/DatasetExportMixin.py +224 -302
- edsl/results/DatasetTree.py +28 -8
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +192 -206
- edsl/results/Results.py +120 -113
- edsl/results/ResultsExportMixin.py +2 -0
- edsl/results/Selector.py +23 -13
- edsl/results/TableDisplay.py +98 -171
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_renderers.py +118 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DirectoryScanner.py +96 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +118 -239
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +90 -193
- edsl/scenarios/ScenarioHtmlMixin.py +4 -3
- edsl/scenarios/ScenarioJoin.py +10 -6
- edsl/scenarios/ScenarioList.py +383 -240
- edsl/scenarios/ScenarioListExportMixin.py +0 -7
- edsl/scenarios/ScenarioListPdfMixin.py +15 -37
- edsl/scenarios/ScenarioSelector.py +156 -0
- edsl/scenarios/__init__.py +1 -2
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +38 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +5 -12
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/Rule.py +5 -4
- edsl/surveys/RuleCollection.py +25 -27
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +199 -771
- edsl/surveys/SurveyCSS.py +20 -8
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +11 -9
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/descriptors.py +6 -2
- edsl/surveys/instructions/ChangeInstruction.py +1 -2
- edsl/surveys/instructions/Instruction.py +4 -13
- edsl/surveys/instructions/InstructionCollection.py +11 -6
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/utilities.py +35 -23
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/METADATA +12 -10
- edsl-0.1.39.dev2.dist-info/RECORD +352 -0
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.39.dev1.dist-info/RECORD +0 -277
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.39.dev1.dist-info → edsl-0.1.39.dev2.dist-info}/WHEEL +0 -0
edsl/results/Result.py
CHANGED
@@ -1,81 +1,61 @@
|
|
1
1
|
# """This module contains the Result class, which captures the result of one interview."""
|
2
2
|
from __future__ import annotations
|
3
|
+
import inspect
|
3
4
|
from collections import UserDict
|
4
|
-
from typing import Any, Type, Callable, Optional
|
5
|
-
from collections import UserDict
|
5
|
+
from typing import Any, Type, Callable, Optional, TYPE_CHECKING, Union
|
6
6
|
from edsl.Base import Base
|
7
7
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
8
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from edsl.agents.Agent import Agent
|
11
|
+
from edsl.scenarios.Scenario import Scenario
|
12
|
+
from edsl.language_models.LanguageModel import LanguageModel
|
13
|
+
from edsl.prompts.Prompt import Prompt
|
14
|
+
from edsl.surveys.Survey import Survey
|
9
15
|
|
10
|
-
class PromptDict(UserDict):
|
11
|
-
"""A dictionary that is used to store the prompt for a given result."""
|
12
|
-
|
13
|
-
def rich_print(self):
|
14
|
-
"""Display an object as a table."""
|
15
|
-
from rich.table import Table
|
16
|
-
|
17
|
-
table = Table(title="")
|
18
|
-
table.add_column("Attribute", style="bold")
|
19
|
-
table.add_column("Value")
|
20
|
-
|
21
|
-
to_display = self
|
22
|
-
for attr_name, attr_value in to_display.items():
|
23
|
-
table.add_row(attr_name, repr(attr_value))
|
24
16
|
|
25
|
-
|
17
|
+
QuestionName = str
|
18
|
+
AnswerValue = Any
|
26
19
|
|
27
20
|
|
28
|
-
|
29
|
-
"""
|
30
|
-
agent_dict = {}
|
21
|
+
class AgentNamer:
|
22
|
+
"""Maintains a registry of agent names to ensure unique naming."""
|
31
23
|
|
32
|
-
def
|
33
|
-
|
34
|
-
nonlocal agent_dict
|
35
|
-
agent_count = len(agent_dict)
|
36
|
-
if id(agent) in agent_dict:
|
37
|
-
return agent_dict[id(agent)]
|
38
|
-
else:
|
39
|
-
agent_dict[id(agent)] = f"Agent_{agent_count}"
|
40
|
-
return agent_dict[id(agent)]
|
24
|
+
def __init__(self):
|
25
|
+
self._registry = {}
|
41
26
|
|
42
|
-
|
27
|
+
def get_name(self, agent: "Agent") -> str:
|
28
|
+
"""Get or create a unique name for an agent."""
|
29
|
+
agent_id = id(agent)
|
30
|
+
if agent_id not in self._registry:
|
31
|
+
self._registry[agent_id] = f"Agent_{len(self._registry)}"
|
32
|
+
return self._registry[agent_id]
|
43
33
|
|
44
34
|
|
45
|
-
|
35
|
+
# Global instance for agent naming
|
36
|
+
agent_namer = AgentNamer().get_name
|
46
37
|
|
47
38
|
|
48
39
|
class Result(Base, UserDict):
|
49
40
|
"""
|
50
41
|
This class captures the result of one interview.
|
51
|
-
|
52
|
-
The answer dictionary has the structure:
|
53
|
-
|
54
|
-
>>> import warnings
|
55
|
-
>>> warnings.simplefilter("ignore", UserWarning)
|
56
|
-
>>> Result.example().answer == {'how_feeling_yesterday': 'Great', 'how_feeling': 'OK'}
|
57
|
-
True
|
58
|
-
|
59
|
-
Its main data is an Agent, a Scenario, a Model, an Iteration, and an Answer.
|
60
|
-
These are stored both in the UserDict and as attributes.
|
61
|
-
|
62
|
-
|
63
42
|
"""
|
64
43
|
|
65
44
|
def __init__(
|
66
45
|
self,
|
67
46
|
agent: "Agent",
|
68
47
|
scenario: "Scenario",
|
69
|
-
model:
|
48
|
+
model: "LanguageModel",
|
70
49
|
iteration: int,
|
71
|
-
answer:
|
72
|
-
prompt: dict[
|
73
|
-
raw_model_response=None,
|
50
|
+
answer: dict[QuestionName, AnswerValue],
|
51
|
+
prompt: dict[QuestionName, str] = None,
|
52
|
+
raw_model_response: Optional[dict] = None,
|
74
53
|
survey: Optional["Survey"] = None,
|
75
|
-
question_to_attributes: Optional[dict] = None,
|
54
|
+
question_to_attributes: Optional[dict[QuestionName, Any]] = None,
|
76
55
|
generated_tokens: Optional[dict] = None,
|
77
56
|
comments_dict: Optional[dict] = None,
|
78
|
-
cache_used_dict: Optional[dict] = None,
|
57
|
+
cache_used_dict: Optional[dict[QuestionName, bool]] = None,
|
58
|
+
indices: Optional[dict] = None,
|
79
59
|
):
|
80
60
|
"""Initialize a Result object.
|
81
61
|
|
@@ -86,26 +66,17 @@ class Result(Base, UserDict):
|
|
86
66
|
:param answer: The answer string.
|
87
67
|
:param prompt: A dictionary of prompts.
|
88
68
|
:param raw_model_response: The raw model response.
|
69
|
+
:param survey: The Survey object.
|
70
|
+
:param question_to_attributes: A dictionary of question attributes.
|
71
|
+
:param generated_tokens: A dictionary of generated tokens.
|
72
|
+
:param comments_dict: A dictionary of comments.
|
73
|
+
:param cache_used_dict: A dictionary of cache usage.
|
74
|
+
:param indices: A dictionary of indices.
|
89
75
|
|
90
76
|
"""
|
91
|
-
|
92
|
-
question_to_attributes
|
93
|
-
|
94
|
-
question_to_attributes = {}
|
95
|
-
|
96
|
-
if survey is not None:
|
97
|
-
question_to_attributes = {
|
98
|
-
q.question_name: {
|
99
|
-
"question_text": q.question_text,
|
100
|
-
"question_type": q.question_type,
|
101
|
-
"question_options": (
|
102
|
-
None
|
103
|
-
if not hasattr(q, "question_options")
|
104
|
-
else q.question_options
|
105
|
-
),
|
106
|
-
}
|
107
|
-
for q in survey.questions
|
108
|
-
}
|
77
|
+
self.question_to_attributes = (
|
78
|
+
question_to_attributes or self._create_question_to_attributes(survey)
|
79
|
+
)
|
109
80
|
|
110
81
|
data = {
|
111
82
|
"agent": agent,
|
@@ -118,81 +89,127 @@ class Result(Base, UserDict):
|
|
118
89
|
"question_to_attributes": question_to_attributes,
|
119
90
|
"generated_tokens": generated_tokens or {},
|
120
91
|
"comments_dict": comments_dict or {},
|
92
|
+
"cache_used_dict": cache_used_dict or {},
|
121
93
|
}
|
122
94
|
super().__init__(**data)
|
123
|
-
|
124
|
-
self.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
table = str(tabulate(data, headers=["keys", "values"], tablefmt="html"))
|
147
|
-
return f"<pre>{table}</pre>"
|
148
|
-
|
149
|
-
###############
|
150
|
-
# Used in Results
|
151
|
-
###############
|
95
|
+
self.indices = indices
|
96
|
+
self._sub_dicts = self._construct_sub_dicts()
|
97
|
+
(
|
98
|
+
self._combined_dict,
|
99
|
+
self._problem_keys,
|
100
|
+
) = self._compute_combined_dict_and_problem_keys()
|
101
|
+
|
102
|
+
@staticmethod
|
103
|
+
def _create_question_to_attributes(survey):
|
104
|
+
"""Create a dictionary of question attributes."""
|
105
|
+
if survey is None:
|
106
|
+
return {}
|
107
|
+
return {
|
108
|
+
q.question_name: {
|
109
|
+
"question_text": q.question_text,
|
110
|
+
"question_type": q.question_type,
|
111
|
+
"question_options": (
|
112
|
+
None if not hasattr(q, "question_options") else q.question_options
|
113
|
+
),
|
114
|
+
}
|
115
|
+
for q in survey.questions
|
116
|
+
}
|
117
|
+
|
152
118
|
@property
|
153
|
-
def
|
154
|
-
"""Return
|
155
|
-
|
156
|
-
|
119
|
+
def agent(self) -> "Agent":
|
120
|
+
"""Return the Agent object."""
|
121
|
+
return self.data["agent"]
|
122
|
+
|
123
|
+
@property
|
124
|
+
def scenario(self) -> "Scenario":
|
125
|
+
"""Return the Scenario object."""
|
126
|
+
return self.data["scenario"]
|
127
|
+
|
128
|
+
@property
|
129
|
+
def model(self) -> "LanguageModel":
|
130
|
+
"""Return the LanguageModel object."""
|
131
|
+
return self.data["model"]
|
132
|
+
|
133
|
+
@property
|
134
|
+
def answer(self) -> dict[QuestionName, AnswerValue]:
|
135
|
+
"""Return the answers."""
|
136
|
+
return self.data["answer"]
|
137
|
+
|
138
|
+
@staticmethod
|
139
|
+
def _create_agent_sub_dict(agent) -> dict:
|
140
|
+
"""Create a dictionary of agent details"""
|
141
|
+
if agent.name is None:
|
142
|
+
agent_name = agent_namer(agent)
|
157
143
|
else:
|
158
|
-
agent_name =
|
159
|
-
|
160
|
-
# comments_dict = {k: v for k, v in self.answer.items() if k.endswith("_comment")}
|
161
|
-
question_text_dict = {}
|
162
|
-
question_options_dict = {}
|
163
|
-
question_type_dict = {}
|
164
|
-
for key, _ in self.answer.items():
|
165
|
-
if key in self.question_to_attributes:
|
166
|
-
# You might be tempted to just use the naked key
|
167
|
-
# but this is a bad idea because it pollutes the namespace
|
168
|
-
question_text_dict[
|
169
|
-
key + "_question_text"
|
170
|
-
] = self.question_to_attributes[key]["question_text"]
|
171
|
-
question_options_dict[
|
172
|
-
key + "_question_options"
|
173
|
-
] = self.question_to_attributes[key]["question_options"]
|
174
|
-
question_type_dict[
|
175
|
-
key + "_question_type"
|
176
|
-
] = self.question_to_attributes[key]["question_type"]
|
144
|
+
agent_name = agent.name
|
177
145
|
|
178
146
|
return {
|
179
|
-
"agent":
|
147
|
+
"agent": agent.traits
|
180
148
|
| {"agent_name": agent_name}
|
181
|
-
| {"agent_instruction":
|
182
|
-
"scenario": self.scenario,
|
183
|
-
"model": self.model.parameters | {"model": self.model.model},
|
184
|
-
"answer": self.answer,
|
185
|
-
"prompt": self.prompt,
|
186
|
-
"raw_model_response": self.raw_model_response,
|
187
|
-
"iteration": {"iteration": self.iteration},
|
188
|
-
"question_text": question_text_dict,
|
189
|
-
"question_options": question_options_dict,
|
190
|
-
"question_type": question_type_dict,
|
191
|
-
"comment": self.comments_dict,
|
192
|
-
"generated_tokens": self.generated_tokens,
|
149
|
+
| {"agent_instruction": agent.instruction},
|
193
150
|
}
|
194
151
|
|
195
|
-
|
152
|
+
@staticmethod
|
153
|
+
def _create_model_sub_dict(model) -> dict:
|
154
|
+
return {
|
155
|
+
"model": model.parameters | {"model": model.model},
|
156
|
+
}
|
157
|
+
|
158
|
+
@staticmethod
|
159
|
+
def _iteration_sub_dict(iteration) -> dict:
|
160
|
+
return {
|
161
|
+
"iteration": {"iteration": iteration},
|
162
|
+
}
|
163
|
+
|
164
|
+
def _construct_sub_dicts(self) -> dict[str, dict]:
|
165
|
+
"""Construct a dictionary of sub-dictionaries for the Result object."""
|
166
|
+
sub_dicts_needing_new_keys = {
|
167
|
+
"question_text": {},
|
168
|
+
"question_options": {},
|
169
|
+
"question_type": {},
|
170
|
+
}
|
171
|
+
|
172
|
+
for question_name in self.data["answer"]:
|
173
|
+
if question_name in self.question_to_attributes:
|
174
|
+
for dictionary_name in sub_dicts_needing_new_keys:
|
175
|
+
new_key = question_name + "_" + dictionary_name
|
176
|
+
sub_dicts_needing_new_keys[dictionary_name][
|
177
|
+
new_key
|
178
|
+
] = self.question_to_attributes[question_name][dictionary_name]
|
179
|
+
|
180
|
+
new_cache_dict = {
|
181
|
+
f"{k}_cache_used": v for k, v in self.data["cache_used_dict"].items()
|
182
|
+
}
|
183
|
+
|
184
|
+
d = {
|
185
|
+
**self._create_agent_sub_dict(self.data["agent"]),
|
186
|
+
**self._create_model_sub_dict(self.data["model"]),
|
187
|
+
**self._iteration_sub_dict(self.data["iteration"]),
|
188
|
+
"scenario": self.data["scenario"],
|
189
|
+
"answer": self.data["answer"],
|
190
|
+
"prompt": self.data["prompt"],
|
191
|
+
"comment": self.data["comments_dict"],
|
192
|
+
"generated_tokens": self.data["generated_tokens"],
|
193
|
+
"raw_model_response": self.data["raw_model_response"],
|
194
|
+
"question_text": sub_dicts_needing_new_keys["question_text"],
|
195
|
+
"question_options": sub_dicts_needing_new_keys["question_options"],
|
196
|
+
"question_type": sub_dicts_needing_new_keys["question_type"],
|
197
|
+
"cache_used": new_cache_dict,
|
198
|
+
}
|
199
|
+
if hasattr(self, "indices") and self.indices is not None:
|
200
|
+
d["agent"].update({"agent_index": self.indices["agent"]})
|
201
|
+
d["scenario"].update({"scenario_index": self.indices["scenario"]})
|
202
|
+
d["model"].update({"model_index": self.indices["model"]})
|
203
|
+
return d
|
204
|
+
|
205
|
+
@property
|
206
|
+
def sub_dicts(self) -> dict[str, dict]:
|
207
|
+
"""Return a dictionary where keys are strings for each of the main class attributes/objects."""
|
208
|
+
if self._sub_dicts is None:
|
209
|
+
self._sub_dicts = self._construct_sub_dicts()
|
210
|
+
return self._sub_dicts
|
211
|
+
|
212
|
+
def check_expression(self, expression: str) -> None:
|
196
213
|
for key in self.problem_keys:
|
197
214
|
if key in expression and not key + "." in expression:
|
198
215
|
raise ValueError(
|
@@ -205,11 +222,13 @@ class Result(Base, UserDict):
|
|
205
222
|
raise NotImplementedError
|
206
223
|
|
207
224
|
@property
|
208
|
-
def problem_keys(self):
|
225
|
+
def problem_keys(self) -> list[str]:
|
209
226
|
"""Return a list of keys that are problematic."""
|
210
227
|
return self._problem_keys
|
211
228
|
|
212
|
-
def _compute_combined_dict_and_problem_keys(
|
229
|
+
def _compute_combined_dict_and_problem_keys(
|
230
|
+
self,
|
231
|
+
) -> tuple[dict[str, Any], list[str]]:
|
213
232
|
combined = {}
|
214
233
|
problem_keys = []
|
215
234
|
for key, sub_dict in self.sub_dicts.items():
|
@@ -222,8 +241,7 @@ class Result(Base, UserDict):
|
|
222
241
|
combined.update({key: sub_dict})
|
223
242
|
# I *think* this allows us to do do things like "answer.how_feelling" i.e., that the evaluator can use
|
224
243
|
# dot notation to access the subdicts.
|
225
|
-
|
226
|
-
self._problem_keys = problem_keys
|
244
|
+
return combined, problem_keys
|
227
245
|
|
228
246
|
@property
|
229
247
|
def combined_dict(self) -> dict[str, Any]:
|
@@ -234,11 +252,14 @@ class Result(Base, UserDict):
|
|
234
252
|
'OK'
|
235
253
|
"""
|
236
254
|
if self._combined_dict is None or self._problem_keys is None:
|
237
|
-
|
255
|
+
(
|
256
|
+
self._combined_dict,
|
257
|
+
self._problem_keys,
|
258
|
+
) = self._compute_combined_dict_and_problem_keys()
|
238
259
|
return self._combined_dict
|
239
260
|
|
240
261
|
@property
|
241
|
-
def problem_keys(self):
|
262
|
+
def problem_keys(self) -> list[str]:
|
242
263
|
"""Return a list of keys that are problematic."""
|
243
264
|
if self._combined_dict is None or self._problem_keys is None:
|
244
265
|
self._compute_combined_dict_and_problem_keys()
|
@@ -278,7 +299,6 @@ class Result(Base, UserDict):
|
|
278
299
|
)
|
279
300
|
problem_keys.append((key, data_type))
|
280
301
|
key = f"{key}_{data_type}"
|
281
|
-
# raise ValueError(f"Key '{key}' is already in the dictionary")
|
282
302
|
d[key] = data_type
|
283
303
|
|
284
304
|
for key, data_type in problem_keys:
|
@@ -287,37 +307,16 @@ class Result(Base, UserDict):
|
|
287
307
|
].pop(key)
|
288
308
|
return d
|
289
309
|
|
290
|
-
def rows(self, index) -> tuple[int, str, str, str]:
|
291
|
-
"""Return a generator of rows for the Result object."""
|
292
|
-
for data_type, subdict in self.sub_dicts.items():
|
293
|
-
for key, value in subdict.items():
|
294
|
-
yield (index, data_type, key, str(value))
|
295
|
-
|
296
|
-
def leaves(self):
|
297
|
-
leaves = []
|
298
|
-
for question_name, answer in self.answer.items():
|
299
|
-
if not question_name.endswith("_comment"):
|
300
|
-
leaves.append(
|
301
|
-
{
|
302
|
-
"question": f"({question_name}): "
|
303
|
-
+ str(
|
304
|
-
self.question_to_attributes[question_name]["question_text"]
|
305
|
-
),
|
306
|
-
"answer": answer,
|
307
|
-
"comment": self.answer.get(question_name + "_comment", ""),
|
308
|
-
"scenario": repr(self.scenario),
|
309
|
-
"agent": repr(self.agent),
|
310
|
-
"model": repr(self.model),
|
311
|
-
"iteration": self.iteration,
|
312
|
-
}
|
313
|
-
)
|
314
|
-
return leaves
|
315
|
-
|
316
|
-
###############
|
317
|
-
# Useful
|
318
|
-
###############
|
319
310
|
def copy(self) -> Result:
|
320
|
-
"""Return a copy of the Result object.
|
311
|
+
"""Return a copy of the Result object.
|
312
|
+
|
313
|
+
>>> r = Result.example()
|
314
|
+
>>> r2 = r.copy()
|
315
|
+
>>> r == r2
|
316
|
+
True
|
317
|
+
>>> id(r) == id(r2)
|
318
|
+
False
|
319
|
+
"""
|
321
320
|
return Result.from_dict(self.to_dict())
|
322
321
|
|
323
322
|
def __eq__(self, other) -> bool:
|
@@ -328,17 +327,16 @@ class Result(Base, UserDict):
|
|
328
327
|
True
|
329
328
|
|
330
329
|
"""
|
331
|
-
return self
|
330
|
+
return hash(self) == hash(other)
|
332
331
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
def to_dict(self, add_edsl_version=True) -> dict[str, Any]:
|
332
|
+
def to_dict(
|
333
|
+
self, add_edsl_version: bool = True, include_cache_info: bool = False
|
334
|
+
) -> dict[str, Any]:
|
337
335
|
"""Return a dictionary representation of the Result object.
|
338
336
|
|
339
337
|
>>> r = Result.example()
|
340
338
|
>>> r.to_dict()['scenario']
|
341
|
-
{'period': 'morning', 'edsl_version': '...', 'edsl_class_name': 'Scenario'}
|
339
|
+
{'period': 'morning', 'scenario_index': 0, 'edsl_version': '...', 'edsl_class_name': 'Scenario'}
|
342
340
|
"""
|
343
341
|
|
344
342
|
def convert_value(value, add_edsl_version=True):
|
@@ -366,21 +364,26 @@ class Result(Base, UserDict):
|
|
366
364
|
d["edsl_version"] = __version__
|
367
365
|
d["edsl_class_name"] = "Result"
|
368
366
|
|
367
|
+
if include_cache_info:
|
368
|
+
d["cache_used_dict"] = self.data["cache_used_dict"]
|
369
|
+
else:
|
370
|
+
d.pop("cache_used_dict", None)
|
371
|
+
|
369
372
|
return d
|
370
373
|
|
371
374
|
def __hash__(self):
|
372
375
|
"""Return a hash of the Result object."""
|
373
376
|
from edsl.utilities.utilities import dict_hash
|
374
377
|
|
375
|
-
return dict_hash(self.to_dict(add_edsl_version=False))
|
378
|
+
return dict_hash(self.to_dict(add_edsl_version=False, include_cache_info=False))
|
376
379
|
|
377
380
|
@classmethod
|
378
381
|
@remove_edsl_version
|
379
382
|
def from_dict(self, json_dict: dict) -> Result:
|
380
383
|
"""Return a Result object from a dictionary representation."""
|
381
384
|
|
382
|
-
from edsl import Agent
|
383
|
-
from edsl import Scenario
|
385
|
+
from edsl.agents.Agent import Agent
|
386
|
+
from edsl.scenarios.Scenario import Scenario
|
384
387
|
from edsl.language_models.LanguageModel import LanguageModel
|
385
388
|
from edsl.prompts.Prompt import Prompt
|
386
389
|
|
@@ -402,51 +405,34 @@ class Result(Base, UserDict):
|
|
402
405
|
question_to_attributes=json_dict.get("question_to_attributes", None),
|
403
406
|
generated_tokens=json_dict.get("generated_tokens", {}),
|
404
407
|
comments_dict=json_dict.get("comments_dict", {}),
|
408
|
+
cache_used_dict=json_dict.get("cache_used_dict", {}),
|
405
409
|
)
|
406
410
|
return result
|
407
411
|
|
408
|
-
def rich_print(self) -> None:
|
409
|
-
"""Display an object as a table."""
|
410
|
-
# from edsl.utilities import print_dict_with_rich
|
411
|
-
from rich import print
|
412
|
-
from rich.table import Table
|
413
|
-
|
414
|
-
table = Table(title="Result")
|
415
|
-
table.add_column("Attribute", style="bold")
|
416
|
-
table.add_column("Value")
|
417
|
-
|
418
|
-
to_display = self.__dict__.copy()
|
419
|
-
data = to_display.pop("data", None)
|
420
|
-
for attr_name, attr_value in to_display.items():
|
421
|
-
if hasattr(attr_value, "rich_print"):
|
422
|
-
table.add_row(attr_name, attr_value.rich_print())
|
423
|
-
elif isinstance(attr_value, dict):
|
424
|
-
a = PromptDict(attr_value)
|
425
|
-
table.add_row(attr_name, a.rich_print())
|
426
|
-
else:
|
427
|
-
table.add_row(attr_name, repr(attr_value))
|
428
|
-
return table
|
429
|
-
|
430
412
|
def __repr__(self):
|
431
413
|
"""Return a string representation of the Result object."""
|
432
|
-
|
414
|
+
params = ", ".join(f"{key}={repr(value)}" for key, value in self.data.items())
|
415
|
+
return f"{self.__class__.__name__}({params})"
|
433
416
|
|
434
417
|
@classmethod
|
435
418
|
def example(cls):
|
436
|
-
"""Return an example Result object.
|
419
|
+
"""Return an example Result object.
|
420
|
+
|
421
|
+
>>> Result.example()
|
422
|
+
Result(...)
|
423
|
+
|
424
|
+
"""
|
437
425
|
from edsl.results.Results import Results
|
438
426
|
|
439
427
|
return Results.example()[0]
|
440
428
|
|
441
|
-
def score(self, scoring_function: Callable) ->
|
429
|
+
def score(self, scoring_function: Callable) -> Union[int, float]:
|
442
430
|
"""Score the result using a passed-in scoring function.
|
443
431
|
|
444
432
|
>>> def f(status): return 1 if status == 'Joyful' else 0
|
445
433
|
>>> Result.example().score(f)
|
446
434
|
1
|
447
435
|
"""
|
448
|
-
import inspect
|
449
|
-
|
450
436
|
signature = inspect.signature(scoring_function)
|
451
437
|
params = {}
|
452
438
|
for k, v in signature.parameters.items():
|