edsl 0.1.33.dev1__py3-none-any.whl → 0.1.33.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/TemplateLoader.py +24 -0
- edsl/__init__.py +8 -4
- edsl/agents/Agent.py +46 -14
- edsl/agents/AgentList.py +43 -0
- edsl/agents/Invigilator.py +125 -212
- edsl/agents/InvigilatorBase.py +140 -32
- edsl/agents/PromptConstructionMixin.py +43 -66
- edsl/agents/__init__.py +1 -0
- edsl/auto/AutoStudy.py +117 -0
- edsl/auto/StageBase.py +230 -0
- edsl/auto/StageGenerateSurvey.py +178 -0
- edsl/auto/StageLabelQuestions.py +125 -0
- edsl/auto/StagePersona.py +61 -0
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -0
- edsl/auto/StagePersonaDimensionValues.py +74 -0
- edsl/auto/StagePersonaDimensions.py +69 -0
- edsl/auto/StageQuestions.py +73 -0
- edsl/auto/SurveyCreatorPipeline.py +21 -0
- edsl/auto/utilities.py +224 -0
- edsl/config.py +38 -39
- edsl/coop/PriceFetcher.py +58 -0
- edsl/coop/coop.py +39 -5
- edsl/data/Cache.py +35 -1
- edsl/data_transfer_models.py +120 -38
- edsl/enums.py +2 -0
- edsl/exceptions/language_models.py +25 -1
- edsl/exceptions/questions.py +62 -5
- edsl/exceptions/results.py +4 -0
- edsl/inference_services/AnthropicService.py +13 -11
- edsl/inference_services/AwsBedrock.py +19 -17
- edsl/inference_services/AzureAI.py +37 -20
- edsl/inference_services/GoogleService.py +16 -12
- edsl/inference_services/GroqService.py +2 -0
- edsl/inference_services/InferenceServiceABC.py +24 -0
- edsl/inference_services/MistralAIService.py +120 -0
- edsl/inference_services/OpenAIService.py +41 -50
- edsl/inference_services/TestService.py +71 -0
- edsl/inference_services/models_available_cache.py +0 -6
- edsl/inference_services/registry.py +4 -0
- edsl/jobs/Answers.py +10 -12
- edsl/jobs/FailedQuestion.py +78 -0
- edsl/jobs/Jobs.py +18 -13
- edsl/jobs/buckets/TokenBucket.py +39 -14
- edsl/jobs/interviews/Interview.py +297 -77
- edsl/jobs/interviews/InterviewExceptionEntry.py +83 -19
- edsl/jobs/interviews/interview_exception_tracking.py +0 -70
- edsl/jobs/interviews/retry_management.py +3 -1
- edsl/jobs/runners/JobsRunnerAsyncio.py +116 -70
- edsl/jobs/runners/JobsRunnerStatusMixin.py +1 -1
- edsl/jobs/tasks/QuestionTaskCreator.py +30 -23
- edsl/jobs/tasks/TaskHistory.py +131 -213
- edsl/language_models/LanguageModel.py +239 -129
- edsl/language_models/ModelList.py +2 -2
- edsl/language_models/RegisterLanguageModelsMeta.py +14 -29
- edsl/language_models/fake_openai_call.py +15 -0
- edsl/language_models/fake_openai_service.py +61 -0
- edsl/language_models/registry.py +15 -2
- edsl/language_models/repair.py +0 -19
- edsl/language_models/utilities.py +61 -0
- edsl/prompts/Prompt.py +52 -2
- edsl/questions/AnswerValidatorMixin.py +23 -26
- edsl/questions/QuestionBase.py +273 -242
- edsl/questions/QuestionBaseGenMixin.py +133 -0
- edsl/questions/QuestionBasePromptsMixin.py +266 -0
- edsl/questions/QuestionBudget.py +6 -0
- edsl/questions/QuestionCheckBox.py +227 -35
- edsl/questions/QuestionExtract.py +98 -27
- edsl/questions/QuestionFreeText.py +46 -29
- edsl/questions/QuestionFunctional.py +7 -0
- edsl/questions/QuestionList.py +141 -22
- edsl/questions/QuestionMultipleChoice.py +173 -64
- edsl/questions/QuestionNumerical.py +87 -46
- edsl/questions/QuestionRank.py +182 -24
- edsl/questions/RegisterQuestionsMeta.py +31 -12
- edsl/questions/ResponseValidatorABC.py +169 -0
- edsl/questions/__init__.py +3 -4
- edsl/questions/decorators.py +21 -0
- edsl/questions/derived/QuestionLikertFive.py +10 -5
- edsl/questions/derived/QuestionLinearScale.py +11 -1
- edsl/questions/derived/QuestionTopK.py +6 -0
- edsl/questions/derived/QuestionYesNo.py +16 -1
- edsl/questions/descriptors.py +43 -7
- edsl/questions/prompt_templates/question_budget.jinja +13 -0
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -0
- edsl/questions/prompt_templates/question_extract.jinja +11 -0
- edsl/questions/prompt_templates/question_free_text.jinja +3 -0
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -0
- edsl/questions/prompt_templates/question_list.jinja +17 -0
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -0
- edsl/questions/prompt_templates/question_numerical.jinja +37 -0
- edsl/questions/question_registry.py +6 -2
- edsl/questions/templates/__init__.py +0 -0
- edsl/questions/templates/checkbox/__init__.py +0 -0
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -0
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -0
- edsl/questions/templates/extract/answering_instructions.jinja +7 -0
- edsl/questions/templates/extract/question_presentation.jinja +1 -0
- edsl/questions/templates/free_text/__init__.py +0 -0
- edsl/questions/templates/free_text/answering_instructions.jinja +0 -0
- edsl/questions/templates/free_text/question_presentation.jinja +1 -0
- edsl/questions/templates/likert_five/__init__.py +0 -0
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -0
- edsl/questions/templates/likert_five/question_presentation.jinja +12 -0
- edsl/questions/templates/linear_scale/__init__.py +0 -0
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -0
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -0
- edsl/questions/templates/list/__init__.py +0 -0
- edsl/questions/templates/list/answering_instructions.jinja +4 -0
- edsl/questions/templates/list/question_presentation.jinja +5 -0
- edsl/questions/templates/multiple_choice/__init__.py +0 -0
- edsl/questions/templates/multiple_choice/answering_instructions.jinja +9 -0
- edsl/questions/templates/multiple_choice/html.jinja +0 -0
- edsl/questions/templates/multiple_choice/question_presentation.jinja +12 -0
- edsl/questions/templates/numerical/__init__.py +0 -0
- edsl/questions/templates/numerical/answering_instructions.jinja +8 -0
- edsl/questions/templates/numerical/question_presentation.jinja +7 -0
- edsl/questions/templates/rank/answering_instructions.jinja +11 -0
- edsl/questions/templates/rank/question_presentation.jinja +15 -0
- edsl/questions/templates/top_k/__init__.py +0 -0
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -0
- edsl/questions/templates/top_k/question_presentation.jinja +22 -0
- edsl/questions/templates/yes_no/__init__.py +0 -0
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -0
- edsl/questions/templates/yes_no/question_presentation.jinja +12 -0
- edsl/results/Dataset.py +20 -0
- edsl/results/DatasetExportMixin.py +41 -47
- edsl/results/DatasetTree.py +145 -0
- edsl/results/Result.py +32 -5
- edsl/results/Results.py +131 -45
- edsl/results/ResultsDBMixin.py +3 -3
- edsl/results/Selector.py +118 -0
- edsl/results/tree_explore.py +115 -0
- edsl/scenarios/Scenario.py +10 -4
- edsl/scenarios/ScenarioList.py +348 -39
- edsl/scenarios/ScenarioListExportMixin.py +9 -0
- edsl/study/SnapShot.py +8 -1
- edsl/surveys/RuleCollection.py +2 -2
- edsl/surveys/Survey.py +634 -315
- edsl/surveys/SurveyExportMixin.py +71 -9
- edsl/surveys/SurveyFlowVisualizationMixin.py +2 -1
- edsl/surveys/SurveyQualtricsImport.py +75 -4
- edsl/surveys/instructions/ChangeInstruction.py +47 -0
- edsl/surveys/instructions/Instruction.py +34 -0
- edsl/surveys/instructions/InstructionCollection.py +77 -0
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/templates/error_reporting/base.html +24 -0
- edsl/templates/error_reporting/exceptions_by_model.html +35 -0
- edsl/templates/error_reporting/exceptions_by_question_name.html +17 -0
- edsl/templates/error_reporting/exceptions_by_type.html +17 -0
- edsl/templates/error_reporting/interview_details.html +111 -0
- edsl/templates/error_reporting/interviews.html +10 -0
- edsl/templates/error_reporting/overview.html +5 -0
- edsl/templates/error_reporting/performance_plot.html +2 -0
- edsl/templates/error_reporting/report.css +74 -0
- edsl/templates/error_reporting/report.html +118 -0
- edsl/templates/error_reporting/report.js +25 -0
- {edsl-0.1.33.dev1.dist-info → edsl-0.1.33.dev2.dist-info}/METADATA +4 -2
- edsl-0.1.33.dev2.dist-info/RECORD +289 -0
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +0 -286
- edsl/utilities/gcp_bucket/simple_example.py +0 -9
- edsl-0.1.33.dev1.dist-info/RECORD +0 -209
- {edsl-0.1.33.dev1.dist-info → edsl-0.1.33.dev2.dist-info}/LICENSE +0 -0
- {edsl-0.1.33.dev1.dist-info → edsl-0.1.33.dev2.dist-info}/WHEEL +0 -0
edsl/coop/coop.py
CHANGED
@@ -53,6 +53,7 @@ class Coop:
|
|
53
53
|
method: str,
|
54
54
|
payload: Optional[dict[str, Any]] = None,
|
55
55
|
params: Optional[dict[str, Any]] = None,
|
56
|
+
timeout: Optional[float] = 5,
|
56
57
|
) -> requests.Response:
|
57
58
|
"""
|
58
59
|
Send a request to the server and return the response.
|
@@ -62,11 +63,16 @@ class Coop:
|
|
62
63
|
method = method.upper()
|
63
64
|
if method in ["GET", "DELETE"]:
|
64
65
|
response = requests.request(
|
65
|
-
method, url, params=params, headers=self.headers
|
66
|
+
method, url, params=params, headers=self.headers, timeout=timeout
|
66
67
|
)
|
67
68
|
elif method in ["POST", "PATCH"]:
|
68
69
|
response = requests.request(
|
69
|
-
method,
|
70
|
+
method,
|
71
|
+
url,
|
72
|
+
params=params,
|
73
|
+
json=payload,
|
74
|
+
headers=self.headers,
|
75
|
+
timeout=timeout,
|
70
76
|
)
|
71
77
|
else:
|
72
78
|
raise Exception(f"Invalid {method=}.")
|
@@ -110,10 +116,18 @@ class Coop:
|
|
110
116
|
def edsl_settings(self) -> dict:
|
111
117
|
"""
|
112
118
|
Retrieve and return the EDSL settings stored on Coop.
|
119
|
+
If no response is received within 5 seconds, return an empty dict.
|
113
120
|
"""
|
114
|
-
|
115
|
-
|
116
|
-
|
121
|
+
from requests.exceptions import Timeout
|
122
|
+
|
123
|
+
try:
|
124
|
+
response = self._send_server_request(
|
125
|
+
uri="api/v0/edsl-settings", method="GET", timeout=5
|
126
|
+
)
|
127
|
+
self._resolve_server_response(response)
|
128
|
+
return response.json()
|
129
|
+
except Timeout:
|
130
|
+
return {}
|
117
131
|
|
118
132
|
################
|
119
133
|
# Objects
|
@@ -625,6 +639,26 @@ class Coop:
|
|
625
639
|
|
626
640
|
return response_json
|
627
641
|
|
642
|
+
def fetch_prices(self) -> dict:
|
643
|
+
from edsl.coop.PriceFetcher import PriceFetcher
|
644
|
+
|
645
|
+
from edsl.config import CONFIG
|
646
|
+
|
647
|
+
if bool(CONFIG.get("EDSL_FETCH_TOKEN_PRICES")):
|
648
|
+
price_fetcher = PriceFetcher()
|
649
|
+
return price_fetcher.fetch_prices()
|
650
|
+
else:
|
651
|
+
return {}
|
652
|
+
|
653
|
+
|
654
|
+
if __name__ == "__main__":
|
655
|
+
sheet_data = fetch_sheet_data()
|
656
|
+
if sheet_data:
|
657
|
+
print(f"Successfully fetched {len(sheet_data)} rows of data.")
|
658
|
+
print("First row:", sheet_data[0])
|
659
|
+
else:
|
660
|
+
print("Failed to fetch sheet data.")
|
661
|
+
|
628
662
|
|
629
663
|
def main():
|
630
664
|
"""
|
edsl/data/Cache.py
CHANGED
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
6
6
|
import json
|
7
7
|
import os
|
8
8
|
import warnings
|
9
|
+
import copy
|
9
10
|
from typing import Optional, Union
|
10
11
|
from edsl.Base import Base
|
11
12
|
from edsl.data.CacheEntry import CacheEntry
|
@@ -88,11 +89,24 @@ class Cache(Base):
|
|
88
89
|
# raise NotImplementedError("This method is not implemented yet.")
|
89
90
|
|
90
91
|
def keys(self):
|
92
|
+
"""
|
93
|
+
>>> from edsl import Cache
|
94
|
+
>>> Cache.example().keys()
|
95
|
+
['5513286eb6967abc0511211f0402587d']
|
96
|
+
"""
|
91
97
|
return list(self.data.keys())
|
92
98
|
|
93
99
|
def values(self):
|
100
|
+
"""
|
101
|
+
>>> from edsl import Cache
|
102
|
+
>>> Cache.example().values()
|
103
|
+
[CacheEntry(...)]
|
104
|
+
"""
|
94
105
|
return list(self.data.values())
|
95
106
|
|
107
|
+
def items(self):
|
108
|
+
return zip(self.keys(), self.values())
|
109
|
+
|
96
110
|
def new_entries_cache(self) -> Cache:
|
97
111
|
"""Return a new Cache object with the new entries."""
|
98
112
|
return Cache(data={**self.new_entries, **self.fetched_data})
|
@@ -160,7 +174,7 @@ class Cache(Base):
|
|
160
174
|
parameters: str,
|
161
175
|
system_prompt: str,
|
162
176
|
user_prompt: str,
|
163
|
-
response:
|
177
|
+
response: dict,
|
164
178
|
iteration: int,
|
165
179
|
) -> str:
|
166
180
|
"""
|
@@ -174,6 +188,15 @@ class Cache(Base):
|
|
174
188
|
* The key-value pair is added to `self.new_entries`
|
175
189
|
* If `immediate_write` is True , the key-value pair is added to `self.data`
|
176
190
|
* If `immediate_write` is False, the key-value pair is added to `self.new_entries_to_write_later`
|
191
|
+
|
192
|
+
>>> from edsl import Cache, Model, Question
|
193
|
+
>>> m = Model("test")
|
194
|
+
>>> c = Cache()
|
195
|
+
>>> len(c)
|
196
|
+
0
|
197
|
+
>>> results = Question.example("free_text").by(m).run(cache = c)
|
198
|
+
>>> len(c)
|
199
|
+
1
|
177
200
|
"""
|
178
201
|
|
179
202
|
entry = CacheEntry(
|
@@ -326,6 +349,17 @@ class Cache(Base):
|
|
326
349
|
for key, value in self.data.items():
|
327
350
|
f.write(json.dumps({key: value.to_dict()}) + "\n")
|
328
351
|
|
352
|
+
def to_scenario_list(self):
|
353
|
+
from edsl import ScenarioList, Scenario
|
354
|
+
|
355
|
+
scenarios = []
|
356
|
+
for key, value in self.data.items():
|
357
|
+
new_d = value.to_dict()
|
358
|
+
new_d["cache_key"] = key
|
359
|
+
s = Scenario(new_d)
|
360
|
+
scenarios.append(s)
|
361
|
+
return ScenarioList(scenarios)
|
362
|
+
|
329
363
|
####################
|
330
364
|
# REMOTE
|
331
365
|
####################
|
edsl/data_transfer_models.py
CHANGED
@@ -1,38 +1,120 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
1
|
+
from typing import NamedTuple, Dict, List, Optional, Any
|
2
|
+
|
3
|
+
|
4
|
+
class ModelInputs(NamedTuple):
|
5
|
+
"This is what was send by the agent to the model"
|
6
|
+
user_prompt: str
|
7
|
+
system_prompt: str
|
8
|
+
encoded_image: Optional[str] = None
|
9
|
+
|
10
|
+
|
11
|
+
class EDSLOutput(NamedTuple):
|
12
|
+
"This is the edsl dictionary that is returned by the model"
|
13
|
+
answer: Any
|
14
|
+
generated_tokens: str
|
15
|
+
comment: Optional[str] = None
|
16
|
+
|
17
|
+
|
18
|
+
class ModelResponse(NamedTuple):
|
19
|
+
"This is the metadata that is returned by the model and includes info about the cache"
|
20
|
+
response: dict
|
21
|
+
cache_used: bool
|
22
|
+
cache_key: str
|
23
|
+
cached_response: Optional[Dict[str, Any]] = None
|
24
|
+
cost: Optional[float] = None
|
25
|
+
|
26
|
+
|
27
|
+
class AgentResponseDict(NamedTuple):
|
28
|
+
edsl_dict: EDSLOutput
|
29
|
+
model_inputs: ModelInputs
|
30
|
+
model_outputs: ModelResponse
|
31
|
+
|
32
|
+
|
33
|
+
class EDSLResultObjectInput(NamedTuple):
|
34
|
+
generated_tokens: str
|
35
|
+
question_name: str
|
36
|
+
prompts: dict
|
37
|
+
cached_response: str
|
38
|
+
raw_model_response: str
|
39
|
+
cache_used: bool
|
40
|
+
cache_key: str
|
41
|
+
answer: Any
|
42
|
+
comment: str
|
43
|
+
validated: bool = False
|
44
|
+
exception_occurred: Exception = None
|
45
|
+
cost: Optional[float] = None
|
46
|
+
|
47
|
+
|
48
|
+
# from collections import UserDict
|
49
|
+
|
50
|
+
|
51
|
+
# class AgentResponseDict(UserDict):
|
52
|
+
# """A dictionary to store the response of the agent to a question."""
|
53
|
+
|
54
|
+
# def __init__(
|
55
|
+
# self,
|
56
|
+
# *,
|
57
|
+
# question_name,
|
58
|
+
# answer,
|
59
|
+
# prompts,
|
60
|
+
# generated_tokens: str,
|
61
|
+
# usage=None,
|
62
|
+
# comment=None,
|
63
|
+
# cached_response=None,
|
64
|
+
# raw_model_response=None,
|
65
|
+
# simple_model_raw_response=None,
|
66
|
+
# cache_used=None,
|
67
|
+
# cache_key=None,
|
68
|
+
# ):
|
69
|
+
# """Initialize the AgentResponseDict object."""
|
70
|
+
# usage = usage or {"prompt_tokens": 0, "completion_tokens": 0}
|
71
|
+
# if generated_tokens is None:
|
72
|
+
# raise ValueError("generated_tokens must be provided")
|
73
|
+
# self.data = {
|
74
|
+
# "answer": answer,
|
75
|
+
# "comment": comment,
|
76
|
+
# "question_name": question_name,
|
77
|
+
# "prompts": prompts,
|
78
|
+
# "usage": usage,
|
79
|
+
# "cached_response": cached_response,
|
80
|
+
# "raw_model_response": raw_model_response,
|
81
|
+
# "simple_model_raw_response": simple_model_raw_response,
|
82
|
+
# "cache_used": cache_used,
|
83
|
+
# "cache_key": cache_key,
|
84
|
+
# "generated_tokens": generated_tokens,
|
85
|
+
# }
|
86
|
+
|
87
|
+
# @property
|
88
|
+
# def data(self):
|
89
|
+
# return self._data
|
90
|
+
|
91
|
+
# @data.setter
|
92
|
+
# def data(self, value):
|
93
|
+
# self._data = value
|
94
|
+
|
95
|
+
# def __getitem__(self, key):
|
96
|
+
# return self.data.get(key, None)
|
97
|
+
|
98
|
+
# def __setitem__(self, key, value):
|
99
|
+
# self.data[key] = value
|
100
|
+
|
101
|
+
# def __delitem__(self, key):
|
102
|
+
# del self.data[key]
|
103
|
+
|
104
|
+
# def __iter__(self):
|
105
|
+
# return iter(self.data)
|
106
|
+
|
107
|
+
# def __len__(self):
|
108
|
+
# return len(self.data)
|
109
|
+
|
110
|
+
# def keys(self):
|
111
|
+
# return self.data.keys()
|
112
|
+
|
113
|
+
# def values(self):
|
114
|
+
# return self.data.values()
|
115
|
+
|
116
|
+
# def items(self):
|
117
|
+
# return self.data.items()
|
118
|
+
|
119
|
+
# def is_this_same_model(self):
|
120
|
+
# return True
|
edsl/enums.py
CHANGED
@@ -62,6 +62,7 @@ class InferenceServiceType(EnumWithChecks):
|
|
62
62
|
GROQ = "groq"
|
63
63
|
AZURE = "azure"
|
64
64
|
OLLAMA = "ollama"
|
65
|
+
MISTRAL = "mistral"
|
65
66
|
|
66
67
|
|
67
68
|
service_to_api_keyname = {
|
@@ -74,6 +75,7 @@ service_to_api_keyname = {
|
|
74
75
|
InferenceServiceType.ANTHROPIC.value: "ANTHROPIC_API_KEY",
|
75
76
|
InferenceServiceType.GROQ.value: "GROQ_API_KEY",
|
76
77
|
InferenceServiceType.BEDROCK.value: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
|
78
|
+
InferenceServiceType.MISTRAL.value: "MISTRAL_API_KEY",
|
77
79
|
}
|
78
80
|
|
79
81
|
|
@@ -1,8 +1,32 @@
|
|
1
1
|
from textwrap import dedent
|
2
|
+
from typing import Optional
|
2
3
|
|
3
4
|
|
4
5
|
class LanguageModelExceptions(Exception):
|
5
|
-
|
6
|
+
explanation = (
|
7
|
+
"This is the base class for all exceptions in the LanguageModel class."
|
8
|
+
)
|
9
|
+
|
10
|
+
def __init__(self, message):
|
11
|
+
super().__init__(message)
|
12
|
+
self.message = message
|
13
|
+
|
14
|
+
|
15
|
+
class LanguageModelNoResponseError(LanguageModelExceptions):
|
16
|
+
explanation = (
|
17
|
+
"""This happens when the LLM API cannot be reached and/or does not respond."""
|
18
|
+
)
|
19
|
+
|
20
|
+
def __init__(self, message):
|
21
|
+
super().__init__(message)
|
22
|
+
|
23
|
+
|
24
|
+
class LanguageModelBadResponseError(LanguageModelExceptions):
|
25
|
+
explanation = """This happens when the LLM API can be reached and responds, does not return a usable answer."""
|
26
|
+
|
27
|
+
def __init__(self, message, response_json: Optional[dict] = None):
|
28
|
+
super().__init__(message)
|
29
|
+
self.response_json = response_json
|
6
30
|
|
7
31
|
|
8
32
|
class LanguageModelNotFound(LanguageModelExceptions):
|
edsl/exceptions/questions.py
CHANGED
@@ -1,16 +1,73 @@
|
|
1
|
+
from typing import Any, SupportsIndex
|
2
|
+
from jinja2 import Template
|
3
|
+
import json
|
4
|
+
|
5
|
+
|
1
6
|
class QuestionErrors(Exception):
|
2
|
-
|
7
|
+
"""
|
8
|
+
Base exception class for question-related errors.
|
9
|
+
"""
|
3
10
|
|
11
|
+
def __init__(self, message="An error occurred with the question"):
|
12
|
+
self.message = message
|
13
|
+
super().__init__(self.message)
|
4
14
|
|
5
|
-
class QuestionCreationValidationError(QuestionErrors):
|
6
|
-
pass
|
7
15
|
|
16
|
+
class QuestionAnswerValidationError(QuestionErrors):
|
17
|
+
documentation = "https://docs.expectedparrot.com/en/latest/exceptions.html"
|
18
|
+
|
19
|
+
explanation = """This when the answer coming from the Language Model does not conform to the expectation for that question type.
|
20
|
+
For example, if the question is a multiple choice question, the answer should be drawn from the list of options provided.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, message="Invalid answer.", data=None, model=None):
|
24
|
+
self.message = message
|
25
|
+
self.data = data
|
26
|
+
self.model = model
|
27
|
+
super().__init__(self.message)
|
28
|
+
|
29
|
+
def __str__(self):
|
30
|
+
return f"""{repr(self)}
|
31
|
+
Data being validated: {self.data}
|
32
|
+
Pydnantic Model: {self.model}.
|
33
|
+
Reported error: {self.message}."""
|
34
|
+
|
35
|
+
def to_html_dict(self):
|
36
|
+
return {
|
37
|
+
"error_type": ("Name of the exception", "p", "/p", self.__class__.__name__),
|
38
|
+
"explaination": ("Explanation", "p", "/p", self.explanation),
|
39
|
+
"edsl answer": (
|
40
|
+
"What model returned",
|
41
|
+
"pre",
|
42
|
+
"/pre",
|
43
|
+
json.dumps(self.data, indent=2),
|
44
|
+
),
|
45
|
+
"validating_model": (
|
46
|
+
"Pydantic model for answers",
|
47
|
+
"pre",
|
48
|
+
"/pre",
|
49
|
+
json.dumps(self.model.model_json_schema(), indent=2),
|
50
|
+
),
|
51
|
+
"error_message": (
|
52
|
+
"Error message Pydantic returned",
|
53
|
+
"p",
|
54
|
+
"/p",
|
55
|
+
self.message,
|
56
|
+
),
|
57
|
+
"documentation_url": (
|
58
|
+
"URL to EDSL docs",
|
59
|
+
f"a href='{self.documentation}'",
|
60
|
+
"/a",
|
61
|
+
self.documentation,
|
62
|
+
),
|
63
|
+
}
|
8
64
|
|
9
|
-
|
65
|
+
|
66
|
+
class QuestionCreationValidationError(QuestionErrors):
|
10
67
|
pass
|
11
68
|
|
12
69
|
|
13
|
-
class
|
70
|
+
class QuestionResponseValidationError(QuestionErrors):
|
14
71
|
pass
|
15
72
|
|
16
73
|
|
edsl/exceptions/results.py
CHANGED
@@ -11,6 +11,11 @@ class AnthropicService(InferenceServiceABC):
|
|
11
11
|
|
12
12
|
_inference_service_ = "anthropic"
|
13
13
|
_env_key_name_ = "ANTHROPIC_API_KEY"
|
14
|
+
key_sequence = ["content", 0, "text"] # ["content"][0]["text"]
|
15
|
+
usage_sequence = ["usage"]
|
16
|
+
input_token_name = "input_tokens"
|
17
|
+
output_token_name = "output_tokens"
|
18
|
+
model_exclude_list = []
|
14
19
|
|
15
20
|
@classmethod
|
16
21
|
def available(cls):
|
@@ -34,6 +39,11 @@ class AnthropicService(InferenceServiceABC):
|
|
34
39
|
Child class of LanguageModel for interacting with OpenAI models
|
35
40
|
"""
|
36
41
|
|
42
|
+
key_sequence = cls.key_sequence
|
43
|
+
usage_sequence = cls.usage_sequence
|
44
|
+
input_token_name = cls.input_token_name
|
45
|
+
output_token_name = cls.output_token_name
|
46
|
+
|
37
47
|
_inference_service_ = cls._inference_service_
|
38
48
|
_model_ = model_name
|
39
49
|
_parameters_ = {
|
@@ -46,6 +56,9 @@ class AnthropicService(InferenceServiceABC):
|
|
46
56
|
"top_logprobs": 3,
|
47
57
|
}
|
48
58
|
|
59
|
+
_tpm = cls.get_tpm(cls)
|
60
|
+
_rpm = cls.get_rpm(cls)
|
61
|
+
|
49
62
|
async def async_execute_model_call(
|
50
63
|
self, user_prompt: str, system_prompt: str = ""
|
51
64
|
) -> dict[str, Any]:
|
@@ -66,17 +79,6 @@ class AnthropicService(InferenceServiceABC):
|
|
66
79
|
)
|
67
80
|
return response.model_dump()
|
68
81
|
|
69
|
-
@staticmethod
|
70
|
-
def parse_response(raw_response: dict[str, Any]) -> str:
|
71
|
-
"""Parses the API response and returns the response text."""
|
72
|
-
response = raw_response["content"][0]["text"]
|
73
|
-
pattern = r"^```json(?:\\n|\n)(.+?)(?:\\n|\n)```$"
|
74
|
-
match = re.match(pattern, response, re.DOTALL)
|
75
|
-
if match:
|
76
|
-
return match.group(1)
|
77
|
-
else:
|
78
|
-
return response
|
79
|
-
|
80
82
|
LLM.__name__ = model_class_name
|
81
83
|
|
82
84
|
return LLM
|
@@ -16,6 +16,18 @@ class AwsBedrockService(InferenceServiceABC):
|
|
16
16
|
_env_key_name_ = (
|
17
17
|
"AWS_ACCESS_KEY_ID" # or any other environment key for AWS credentials
|
18
18
|
)
|
19
|
+
key_sequence = ["output", "message", "content", 0, "text"]
|
20
|
+
input_token_name = "inputTokens"
|
21
|
+
output_token_name = "outputTokens"
|
22
|
+
usage_sequence = ["usage"]
|
23
|
+
model_exclude_list = [
|
24
|
+
"ai21.j2-grande-instruct",
|
25
|
+
"ai21.j2-jumbo-instruct",
|
26
|
+
"ai21.j2-mid",
|
27
|
+
"ai21.j2-mid-v1",
|
28
|
+
"ai21.j2-ultra",
|
29
|
+
"ai21.j2-ultra-v1",
|
30
|
+
]
|
19
31
|
|
20
32
|
@classmethod
|
21
33
|
def available(cls):
|
@@ -28,7 +40,7 @@ class AwsBedrockService(InferenceServiceABC):
|
|
28
40
|
else:
|
29
41
|
all_models_ids = cls._models_list_cache
|
30
42
|
|
31
|
-
return all_models_ids
|
43
|
+
return [m for m in all_models_ids if m not in cls.model_exclude_list]
|
32
44
|
|
33
45
|
@classmethod
|
34
46
|
def create_model(
|
@@ -42,6 +54,8 @@ class AwsBedrockService(InferenceServiceABC):
|
|
42
54
|
Child class of LanguageModel for interacting with AWS Bedrock models.
|
43
55
|
"""
|
44
56
|
|
57
|
+
key_sequence = cls.key_sequence
|
58
|
+
usage_sequence = cls.usage_sequence
|
45
59
|
_inference_service_ = cls._inference_service_
|
46
60
|
_model_ = model_name
|
47
61
|
_parameters_ = {
|
@@ -49,6 +63,10 @@ class AwsBedrockService(InferenceServiceABC):
|
|
49
63
|
"max_tokens": 512,
|
50
64
|
"top_p": 0.9,
|
51
65
|
}
|
66
|
+
input_token_name = cls.input_token_name
|
67
|
+
output_token_name = cls.output_token_name
|
68
|
+
_rpm = cls.get_rpm(cls)
|
69
|
+
_tpm = cls.get_tpm(cls)
|
52
70
|
|
53
71
|
async def async_execute_model_call(
|
54
72
|
self, user_prompt: str, system_prompt: str = ""
|
@@ -89,22 +107,6 @@ class AwsBedrockService(InferenceServiceABC):
|
|
89
107
|
print(e)
|
90
108
|
return {"error": str(e)}
|
91
109
|
|
92
|
-
@staticmethod
|
93
|
-
def parse_response(raw_response: dict[str, Any]) -> str:
|
94
|
-
"""Parses the API response and returns the response text."""
|
95
|
-
if "output" in raw_response and "message" in raw_response["output"]:
|
96
|
-
response = raw_response["output"]["message"]["content"][0]["text"]
|
97
|
-
pattern = r"^```json(?:\\n|\n)(.+?)(?:\\n|\n)```$"
|
98
|
-
match = re.match(pattern, response, re.DOTALL)
|
99
|
-
if match:
|
100
|
-
return match.group(1)
|
101
|
-
else:
|
102
|
-
out = fix_partial_correct_response(response)
|
103
|
-
if "error" not in out:
|
104
|
-
response = out["extracted_json"]
|
105
|
-
return response
|
106
|
-
return "Error parsing response"
|
107
|
-
|
108
110
|
LLM.__name__ = model_class_name
|
109
111
|
|
110
112
|
return LLM
|
@@ -25,11 +25,22 @@ def json_handle_none(value: Any) -> Any:
|
|
25
25
|
class AzureAIService(InferenceServiceABC):
|
26
26
|
"""Azure AI service class."""
|
27
27
|
|
28
|
+
# key_sequence = ["content", 0, "text"] # ["content"][0]["text"]
|
29
|
+
key_sequence = ["choices", 0, "message", "content"]
|
30
|
+
usage_sequence = ["usage"]
|
31
|
+
input_token_name = "prompt_tokens"
|
32
|
+
output_token_name = "completion_tokens"
|
33
|
+
|
28
34
|
_inference_service_ = "azure"
|
29
35
|
_env_key_name_ = (
|
30
36
|
"AZURE_ENDPOINT_URL_AND_KEY" # Environment variable for Azure API key
|
31
37
|
)
|
32
38
|
_model_id_to_endpoint_and_key = {}
|
39
|
+
model_exclude_list = [
|
40
|
+
"Cohere-command-r-plus-xncmg",
|
41
|
+
"Mistral-Nemo-klfsi",
|
42
|
+
"Mistral-large-2407-ojfld",
|
43
|
+
]
|
33
44
|
|
34
45
|
@classmethod
|
35
46
|
def available(cls):
|
@@ -82,7 +93,7 @@ class AzureAIService(InferenceServiceABC):
|
|
82
93
|
|
83
94
|
except Exception as e:
|
84
95
|
raise e
|
85
|
-
return out
|
96
|
+
return [m for m in out if m not in cls.model_exclude_list]
|
86
97
|
|
87
98
|
@classmethod
|
88
99
|
def create_model(
|
@@ -96,6 +107,10 @@ class AzureAIService(InferenceServiceABC):
|
|
96
107
|
Child class of LanguageModel for interacting with Azure OpenAI models.
|
97
108
|
"""
|
98
109
|
|
110
|
+
key_sequence = cls.key_sequence
|
111
|
+
usage_sequence = cls.usage_sequence
|
112
|
+
input_token_name = cls.input_token_name
|
113
|
+
output_token_name = cls.output_token_name
|
99
114
|
_inference_service_ = cls._inference_service_
|
100
115
|
_model_ = model_name
|
101
116
|
_parameters_ = {
|
@@ -103,6 +118,8 @@ class AzureAIService(InferenceServiceABC):
|
|
103
118
|
"max_tokens": 512,
|
104
119
|
"top_p": 0.9,
|
105
120
|
}
|
121
|
+
_rpm = cls.get_rpm(cls)
|
122
|
+
_tpm = cls.get_tpm(cls)
|
106
123
|
|
107
124
|
async def async_execute_model_call(
|
108
125
|
self, user_prompt: str, system_prompt: str = ""
|
@@ -172,25 +189,25 @@ class AzureAIService(InferenceServiceABC):
|
|
172
189
|
)
|
173
190
|
return response.model_dump()
|
174
191
|
|
175
|
-
@staticmethod
|
176
|
-
def parse_response(raw_response: dict[str, Any]) -> str:
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
192
|
+
# @staticmethod
|
193
|
+
# def parse_response(raw_response: dict[str, Any]) -> str:
|
194
|
+
# """Parses the API response and returns the response text."""
|
195
|
+
# if (
|
196
|
+
# raw_response
|
197
|
+
# and "choices" in raw_response
|
198
|
+
# and raw_response["choices"]
|
199
|
+
# ):
|
200
|
+
# response = raw_response["choices"][0]["message"]["content"]
|
201
|
+
# pattern = r"^```json(?:\\n|\n)(.+?)(?:\\n|\n)```$"
|
202
|
+
# match = re.match(pattern, response, re.DOTALL)
|
203
|
+
# if match:
|
204
|
+
# return match.group(1)
|
205
|
+
# else:
|
206
|
+
# out = fix_partial_correct_response(response)
|
207
|
+
# if "error" not in out:
|
208
|
+
# response = out["extracted_json"]
|
209
|
+
# return response
|
210
|
+
# return "Error parsing response"
|
194
211
|
|
195
212
|
LLM.__name__ = model_class_name
|
196
213
|
|