edsl 0.1.59__py3-none-any.whl → 0.1.60__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/__version__.py +1 -1
- edsl/base/data_transfer_models.py +5 -0
- edsl/base/enums.py +7 -2
- edsl/dataset/dataset_operations_mixin.py +2 -2
- edsl/inference_services/services/__init__.py +3 -1
- edsl/inference_services/services/open_ai_service_v2.py +243 -0
- edsl/jobs/data_structures.py +3 -0
- edsl/key_management/key_lookup_builder.py +25 -3
- edsl/language_models/language_model.py +2 -1
- edsl/language_models/raw_response_handler.py +126 -7
- edsl/results/result.py +37 -0
- edsl/results/results.py +1 -0
- {edsl-0.1.59.dist-info → edsl-0.1.60.dist-info}/METADATA +2 -2
- {edsl-0.1.59.dist-info → edsl-0.1.60.dist-info}/RECORD +17 -16
- {edsl-0.1.59.dist-info → edsl-0.1.60.dist-info}/LICENSE +0 -0
- {edsl-0.1.59.dist-info → edsl-0.1.60.dist-info}/WHEEL +0 -0
- {edsl-0.1.59.dist-info → edsl-0.1.60.dist-info}/entry_points.txt +0 -0
edsl/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.60"
|
@@ -17,6 +17,7 @@ class EDSLOutput(NamedTuple):
|
|
17
17
|
answer: Any
|
18
18
|
generated_tokens: str
|
19
19
|
comment: Optional[str] = None
|
20
|
+
reasoning_summary: Optional[Any] = None
|
20
21
|
|
21
22
|
|
22
23
|
class ModelResponse(NamedTuple):
|
@@ -49,6 +50,7 @@ class EDSLResultObjectInput(NamedTuple):
|
|
49
50
|
cache_key: str
|
50
51
|
answer: Any
|
51
52
|
comment: str
|
53
|
+
reasoning_summary: Optional[Any] = None
|
52
54
|
validated: bool = False
|
53
55
|
exception_occurred: Exception = None
|
54
56
|
input_tokens: Optional[int] = None
|
@@ -96,12 +98,15 @@ class Answers(UserDict):
|
|
96
98
|
answer = response.answer
|
97
99
|
comment = response.comment
|
98
100
|
generated_tokens = response.generated_tokens
|
101
|
+
reasoning_summary = response.reasoning_summary
|
99
102
|
# record the answer
|
100
103
|
if generated_tokens:
|
101
104
|
self[question.question_name + "_generated_tokens"] = generated_tokens
|
102
105
|
self[question.question_name] = answer
|
103
106
|
if comment:
|
104
107
|
self[question.question_name + "_comment"] = comment
|
108
|
+
if reasoning_summary:
|
109
|
+
self[question.question_name + "_reasoning_summary"] = reasoning_summary
|
105
110
|
|
106
111
|
def replace_missing_answers_with_none(self, survey: "Survey") -> None:
|
107
112
|
"""Replace missing answers with None. Answers can be missing if the agent skips a question."""
|
edsl/base/enums.py
CHANGED
@@ -57,6 +57,7 @@ class InferenceServiceType(EnumWithChecks):
|
|
57
57
|
DEEP_INFRA = "deep_infra"
|
58
58
|
REPLICATE = "replicate"
|
59
59
|
OPENAI = "openai"
|
60
|
+
OPENAI_V2 = "openai_v2"
|
60
61
|
GOOGLE = "google"
|
61
62
|
TEST = "test"
|
62
63
|
ANTHROPIC = "anthropic"
|
@@ -77,6 +78,7 @@ InferenceServiceLiteral = Literal[
|
|
77
78
|
"deep_infra",
|
78
79
|
"replicate",
|
79
80
|
"openai",
|
81
|
+
"openai_v2",
|
80
82
|
"google",
|
81
83
|
"test",
|
82
84
|
"anthropic",
|
@@ -93,6 +95,7 @@ InferenceServiceLiteral = Literal[
|
|
93
95
|
available_models_urls = {
|
94
96
|
"anthropic": "https://docs.anthropic.com/en/docs/about-claude/models",
|
95
97
|
"openai": "https://platform.openai.com/docs/models/gp",
|
98
|
+
"openai_v2": "https://platform.openai.com/docs/models/gp",
|
96
99
|
"groq": "https://console.groq.com/docs/models",
|
97
100
|
"google": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models",
|
98
101
|
}
|
@@ -102,6 +105,7 @@ service_to_api_keyname = {
|
|
102
105
|
InferenceServiceType.DEEP_INFRA.value: "DEEP_INFRA_API_KEY",
|
103
106
|
InferenceServiceType.REPLICATE.value: "TBD",
|
104
107
|
InferenceServiceType.OPENAI.value: "OPENAI_API_KEY",
|
108
|
+
InferenceServiceType.OPENAI_V2.value: "OPENAI_API_KEY",
|
105
109
|
InferenceServiceType.GOOGLE.value: "GOOGLE_API_KEY",
|
106
110
|
InferenceServiceType.TEST.value: "TBD",
|
107
111
|
InferenceServiceType.ANTHROPIC.value: "ANTHROPIC_API_KEY",
|
@@ -135,7 +139,7 @@ class TokenPricing:
|
|
135
139
|
and self.prompt_token_price == other.prompt_token_price
|
136
140
|
and self.completion_token_price == other.completion_token_price
|
137
141
|
)
|
138
|
-
|
142
|
+
|
139
143
|
@classmethod
|
140
144
|
def example(cls) -> "TokenPricing":
|
141
145
|
"""Return an example TokenPricing object."""
|
@@ -145,6 +149,7 @@ class TokenPricing:
|
|
145
149
|
completion_token_price_per_k=0.03,
|
146
150
|
)
|
147
151
|
|
152
|
+
|
148
153
|
pricing = {
|
149
154
|
"dbrx-instruct": TokenPricing(
|
150
155
|
model_name="dbrx-instruct",
|
@@ -212,4 +217,4 @@ def get_token_pricing(model_name):
|
|
212
217
|
model_name=model_name,
|
213
218
|
prompt_token_price_per_k=0.0,
|
214
219
|
completion_token_price_per_k=0.0,
|
215
|
-
)
|
220
|
+
)
|
@@ -357,7 +357,7 @@ class DataOperationsBase:
|
|
357
357
|
4
|
358
358
|
>>> engine = Results.example()._db(shape = "long")
|
359
359
|
>>> len(engine.execute(text("SELECT * FROM self")).fetchall())
|
360
|
-
|
360
|
+
212
|
361
361
|
"""
|
362
362
|
# Import needed for database connection
|
363
363
|
from sqlalchemy import create_engine
|
@@ -442,7 +442,7 @@ class DataOperationsBase:
|
|
442
442
|
|
443
443
|
# Using long format
|
444
444
|
>>> len(r.sql("SELECT * FROM self", shape="long"))
|
445
|
-
|
445
|
+
212
|
446
446
|
"""
|
447
447
|
import pandas as pd
|
448
448
|
|
@@ -8,6 +8,7 @@ from .groq_service import GroqService
|
|
8
8
|
from .mistral_ai_service import MistralAIService
|
9
9
|
from .ollama_service import OllamaService
|
10
10
|
from .open_ai_service import OpenAIService
|
11
|
+
from .open_ai_service_v2 import OpenAIServiceV2
|
11
12
|
from .perplexity_service import PerplexityService
|
12
13
|
from .test_service import TestService
|
13
14
|
from .together_ai_service import TogetherAIService
|
@@ -24,8 +25,9 @@ __all__ = [
|
|
24
25
|
"MistralAIService",
|
25
26
|
"OllamaService",
|
26
27
|
"OpenAIService",
|
28
|
+
"OpenAIServiceV2",
|
27
29
|
"PerplexityService",
|
28
30
|
"TestService",
|
29
31
|
"TogetherAIService",
|
30
32
|
"XAIService",
|
31
|
-
]
|
33
|
+
]
|
@@ -0,0 +1,243 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, List, Optional, Dict, NewType, TYPE_CHECKING
|
3
|
+
import os
|
4
|
+
|
5
|
+
import openai
|
6
|
+
|
7
|
+
from ..inference_service_abc import InferenceServiceABC
|
8
|
+
|
9
|
+
# Use TYPE_CHECKING to avoid circular imports at runtime
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from ...language_models import LanguageModel
|
12
|
+
from ..rate_limits_cache import rate_limits
|
13
|
+
|
14
|
+
# Default to completions API but can use responses API with parameter
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from ....scenarios.file_store import FileStore as Files
|
18
|
+
from ....invigilators.invigilator_base import InvigilatorBase as InvigilatorAI
|
19
|
+
|
20
|
+
|
21
|
+
APIToken = NewType("APIToken", str)
|
22
|
+
|
23
|
+
|
24
|
+
class OpenAIServiceV2(InferenceServiceABC):
|
25
|
+
"""OpenAI service class using the Responses API."""
|
26
|
+
|
27
|
+
_inference_service_ = "openai_v2"
|
28
|
+
_env_key_name_ = "OPENAI_API_KEY"
|
29
|
+
_base_url_ = None
|
30
|
+
|
31
|
+
_sync_client_ = openai.OpenAI
|
32
|
+
_async_client_ = openai.AsyncOpenAI
|
33
|
+
|
34
|
+
_sync_client_instances: Dict[APIToken, openai.OpenAI] = {}
|
35
|
+
_async_client_instances: Dict[APIToken, openai.AsyncOpenAI] = {}
|
36
|
+
|
37
|
+
# sequence to extract text from response.output
|
38
|
+
key_sequence = ["output", 1, "content", 0, "text"]
|
39
|
+
usage_sequence = ["usage"]
|
40
|
+
# sequence to extract reasoning summary from response.output
|
41
|
+
reasoning_sequence = ["output", 0, "summary"]
|
42
|
+
input_token_name = "prompt_tokens"
|
43
|
+
output_token_name = "completion_tokens"
|
44
|
+
|
45
|
+
available_models_url = "https://platform.openai.com/docs/models/gp"
|
46
|
+
|
47
|
+
def __init_subclass__(cls, **kwargs):
|
48
|
+
super().__init_subclass__(**kwargs)
|
49
|
+
cls._sync_client_instances = {}
|
50
|
+
cls._async_client_instances = {}
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def sync_client(cls, api_key: str) -> openai.OpenAI:
|
54
|
+
if api_key not in cls._sync_client_instances:
|
55
|
+
client = cls._sync_client_(
|
56
|
+
api_key=api_key,
|
57
|
+
base_url=cls._base_url_,
|
58
|
+
)
|
59
|
+
cls._sync_client_instances[api_key] = client
|
60
|
+
return cls._sync_client_instances[api_key]
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def async_client(cls, api_key: str) -> openai.AsyncOpenAI:
|
64
|
+
if api_key not in cls._async_client_instances:
|
65
|
+
client = cls._async_client_(
|
66
|
+
api_key=api_key,
|
67
|
+
base_url=cls._base_url_,
|
68
|
+
)
|
69
|
+
cls._async_client_instances[api_key] = client
|
70
|
+
return cls._async_client_instances[api_key]
|
71
|
+
|
72
|
+
model_exclude_list = [
|
73
|
+
"whisper-1",
|
74
|
+
"davinci-002",
|
75
|
+
"dall-e-2",
|
76
|
+
"tts-1-hd-1106",
|
77
|
+
"tts-1-hd",
|
78
|
+
"dall-e-3",
|
79
|
+
"tts-1",
|
80
|
+
"babbage-002",
|
81
|
+
"tts-1-1106",
|
82
|
+
"text-embedding-3-large",
|
83
|
+
"text-embedding-3-small",
|
84
|
+
"text-embedding-ada-002",
|
85
|
+
"ft:davinci-002:mit-horton-lab::8OfuHgoo",
|
86
|
+
"gpt-3.5-turbo-instruct-0914",
|
87
|
+
"gpt-3.5-turbo-instruct",
|
88
|
+
]
|
89
|
+
_models_list_cache: List[str] = []
|
90
|
+
|
91
|
+
@classmethod
|
92
|
+
def get_model_list(cls, api_key: str | None = None) -> List[str]:
|
93
|
+
if api_key is None:
|
94
|
+
api_key = os.getenv(cls._env_key_name_)
|
95
|
+
raw = cls.sync_client(api_key).models.list()
|
96
|
+
return raw.data if hasattr(raw, "data") else raw
|
97
|
+
|
98
|
+
@classmethod
|
99
|
+
def available(cls, api_token: str | None = None) -> List[str]:
|
100
|
+
if api_token is None:
|
101
|
+
api_token = os.getenv(cls._env_key_name_)
|
102
|
+
if not cls._models_list_cache:
|
103
|
+
data = cls.get_model_list(api_key=api_token)
|
104
|
+
cls._models_list_cache = [
|
105
|
+
m.id for m in data if m.id not in cls.model_exclude_list
|
106
|
+
]
|
107
|
+
return cls._models_list_cache
|
108
|
+
|
109
|
+
@classmethod
|
110
|
+
def create_model(
|
111
|
+
cls,
|
112
|
+
model_name: str,
|
113
|
+
model_class_name: str | None = None,
|
114
|
+
) -> LanguageModel:
|
115
|
+
if model_class_name is None:
|
116
|
+
model_class_name = cls.to_class_name(model_name)
|
117
|
+
|
118
|
+
from ...language_models import LanguageModel
|
119
|
+
|
120
|
+
class LLM(LanguageModel):
|
121
|
+
"""Child class for OpenAI Responses API"""
|
122
|
+
|
123
|
+
key_sequence = cls.key_sequence
|
124
|
+
usage_sequence = cls.usage_sequence
|
125
|
+
reasoning_sequence = cls.reasoning_sequence
|
126
|
+
input_token_name = cls.input_token_name
|
127
|
+
output_token_name = cls.output_token_name
|
128
|
+
_inference_service_ = cls._inference_service_
|
129
|
+
_model_ = model_name
|
130
|
+
_parameters_ = {
|
131
|
+
"temperature": 0.5,
|
132
|
+
"max_tokens": 2000,
|
133
|
+
"top_p": 1,
|
134
|
+
"frequency_penalty": 0,
|
135
|
+
"presence_penalty": 0,
|
136
|
+
"logprobs": False,
|
137
|
+
"top_logprobs": 3,
|
138
|
+
}
|
139
|
+
|
140
|
+
def sync_client(self) -> openai.OpenAI:
|
141
|
+
return cls.sync_client(api_key=self.api_token)
|
142
|
+
|
143
|
+
def async_client(self) -> openai.AsyncOpenAI:
|
144
|
+
return cls.async_client(api_key=self.api_token)
|
145
|
+
|
146
|
+
@classmethod
|
147
|
+
def available(cls) -> list[str]:
|
148
|
+
return cls.sync_client().models.list().data
|
149
|
+
|
150
|
+
def get_headers(self) -> dict[str, Any]:
|
151
|
+
client = self.sync_client()
|
152
|
+
response = client.responses.with_raw_response.create(
|
153
|
+
model=self.model,
|
154
|
+
input=[{"role": "user", "content": "Say this is a test"}],
|
155
|
+
store=False,
|
156
|
+
)
|
157
|
+
return dict(response.headers)
|
158
|
+
|
159
|
+
def get_rate_limits(self) -> dict[str, Any]:
|
160
|
+
try:
|
161
|
+
headers = rate_limits.get("openai", self.get_headers())
|
162
|
+
except Exception:
|
163
|
+
return {"rpm": 10000, "tpm": 2000000}
|
164
|
+
return {
|
165
|
+
"rpm": int(headers["x-ratelimit-limit-requests"]),
|
166
|
+
"tpm": int(headers["x-ratelimit-limit-tokens"]),
|
167
|
+
}
|
168
|
+
|
169
|
+
async def async_execute_model_call(
|
170
|
+
self,
|
171
|
+
user_prompt: str,
|
172
|
+
system_prompt: str = "",
|
173
|
+
files_list: Optional[List[Files]] = None,
|
174
|
+
invigilator: Optional[InvigilatorAI] = None,
|
175
|
+
) -> dict[str, Any]:
|
176
|
+
content = user_prompt
|
177
|
+
if files_list:
|
178
|
+
# embed files as separate inputs
|
179
|
+
content = [{"type": "text", "text": user_prompt}]
|
180
|
+
for f in files_list:
|
181
|
+
content.append(
|
182
|
+
{
|
183
|
+
"type": "image_url",
|
184
|
+
"image_url": {
|
185
|
+
"url": f"data:{f.mime_type};base64,{f.base64_string}"
|
186
|
+
},
|
187
|
+
}
|
188
|
+
)
|
189
|
+
# build input sequence
|
190
|
+
messages: Any
|
191
|
+
if system_prompt and not self.omit_system_prompt_if_empty:
|
192
|
+
messages = [
|
193
|
+
{"role": "system", "content": system_prompt},
|
194
|
+
{"role": "user", "content": content},
|
195
|
+
]
|
196
|
+
else:
|
197
|
+
messages = [{"role": "user", "content": content}]
|
198
|
+
|
199
|
+
# All OpenAI models with the responses API use these base parameters
|
200
|
+
params = {
|
201
|
+
"model": self.model,
|
202
|
+
"input": messages,
|
203
|
+
"temperature": self.temperature,
|
204
|
+
"top_p": self.top_p,
|
205
|
+
"store": False,
|
206
|
+
}
|
207
|
+
|
208
|
+
# Check if this is a reasoning model (o-series models)
|
209
|
+
is_reasoning_model = any(tag in self.model for tag in ["o1", "o1-mini", "o3", "o3-mini", "o1-pro", "o4-mini"])
|
210
|
+
|
211
|
+
# Only add reasoning parameter for reasoning models
|
212
|
+
if is_reasoning_model:
|
213
|
+
params["reasoning"] = {"summary": "auto"}
|
214
|
+
|
215
|
+
# For all models using the responses API, use max_output_tokens
|
216
|
+
# instead of max_tokens (which is for the completions API)
|
217
|
+
params["max_output_tokens"] = self.max_tokens
|
218
|
+
|
219
|
+
# Specifically for o-series, we also set temperature to 1
|
220
|
+
if is_reasoning_model:
|
221
|
+
params["temperature"] = 1
|
222
|
+
|
223
|
+
client = self.async_client()
|
224
|
+
try:
|
225
|
+
response = await client.responses.create(**params)
|
226
|
+
|
227
|
+
except Exception as e:
|
228
|
+
return {"message": str(e)}
|
229
|
+
|
230
|
+
# convert to dict
|
231
|
+
response_dict = response.model_dump()
|
232
|
+
return response_dict
|
233
|
+
|
234
|
+
LLM.__name__ = model_class_name
|
235
|
+
return LLM
|
236
|
+
|
237
|
+
@staticmethod
|
238
|
+
def _create_reasoning_sequence():
|
239
|
+
"""Create the reasoning sequence for extracting reasoning summaries from model responses."""
|
240
|
+
# For OpenAI responses, the reasoning summary is typically found at:
|
241
|
+
# ["output", 0, "summary"]
|
242
|
+
# This is the path to the 'summary' field in the first item of the 'output' array
|
243
|
+
return ["output", 0, "summary"]
|
edsl/jobs/data_structures.py
CHANGED
@@ -213,6 +213,9 @@ class Answers(UserDict):
|
|
213
213
|
if comment:
|
214
214
|
self[question.question_name + "_comment"] = comment
|
215
215
|
|
216
|
+
if getattr(response, "reasoning_summary", None):
|
217
|
+
self[question.question_name + "_reasoning_summary"] = response.reasoning_summary
|
218
|
+
|
216
219
|
def replace_missing_answers_with_none(self, survey: "Survey") -> None:
|
217
220
|
"""
|
218
221
|
Replace missing answers with None for all questions in the survey.
|
@@ -363,13 +363,35 @@ class KeyLookupBuilder:
|
|
363
363
|
>>> builder._add_api_key("OPENAI_API_KEY", "sk-1234", "env")
|
364
364
|
>>> 'sk-1234' == builder.key_data["openai"][-1].value
|
365
365
|
True
|
366
|
+
>>> 'sk-1234' == builder.key_data["openai_v2"][-1].value
|
367
|
+
True
|
366
368
|
"""
|
367
369
|
service = api_keyname_to_service[key]
|
368
370
|
new_entry = APIKeyEntry(service=service, name=key, value=value, source=source)
|
369
|
-
|
370
|
-
|
371
|
+
|
372
|
+
# Special case for OPENAI_API_KEY - add to both openai and openai_v2
|
373
|
+
if key == "OPENAI_API_KEY":
|
374
|
+
# Add to openai service
|
375
|
+
openai_service = "openai"
|
376
|
+
openai_entry = APIKeyEntry(service=openai_service, name=key, value=value, source=source)
|
377
|
+
if openai_service not in self.key_data:
|
378
|
+
self.key_data[openai_service] = [openai_entry]
|
379
|
+
else:
|
380
|
+
self.key_data[openai_service].append(openai_entry)
|
381
|
+
|
382
|
+
# Add to openai_v2 service
|
383
|
+
openai_v2_service = "openai_v2"
|
384
|
+
openai_v2_entry = APIKeyEntry(service=openai_v2_service, name=key, value=value, source=source)
|
385
|
+
if openai_v2_service not in self.key_data:
|
386
|
+
self.key_data[openai_v2_service] = [openai_v2_entry]
|
387
|
+
else:
|
388
|
+
self.key_data[openai_v2_service].append(openai_v2_entry)
|
371
389
|
else:
|
372
|
-
|
390
|
+
# Normal case for all other API keys
|
391
|
+
if service not in self.key_data:
|
392
|
+
self.key_data[service] = [new_entry]
|
393
|
+
else:
|
394
|
+
self.key_data[service].append(new_entry)
|
373
395
|
|
374
396
|
def update_from_dict(self, d: dict) -> None:
|
375
397
|
"""
|
@@ -174,7 +174,8 @@ class LanguageModel(
|
|
174
174
|
"""
|
175
175
|
key_sequence = cls.key_sequence
|
176
176
|
usage_sequence = cls.usage_sequence if hasattr(cls, "usage_sequence") else None
|
177
|
-
|
177
|
+
reasoning_sequence = cls.reasoning_sequence if hasattr(cls, "reasoning_sequence") else None
|
178
|
+
return RawResponseHandler(key_sequence, usage_sequence, reasoning_sequence)
|
178
179
|
|
179
180
|
def __init__(
|
180
181
|
self,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Optional, Any
|
2
|
+
from typing import Optional, Any, List
|
3
3
|
from .exceptions import (
|
4
4
|
LanguageModelBadResponseError,
|
5
5
|
LanguageModelTypeError,
|
@@ -41,10 +41,13 @@ def _extract_item_from_raw_response(data, sequence):
|
|
41
41
|
current_data = current_data[key]
|
42
42
|
except Exception as e:
|
43
43
|
path = " -> ".join(map(str, sequence[: i + 1]))
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
# Create a safe error message that won't be None
|
46
|
+
if "error" in data and data["error"] is not None:
|
47
|
+
msg = str(data["error"])
|
46
48
|
else:
|
47
49
|
msg = f"Error accessing path: {path}. {str(e)}. Full response is: '{data}'"
|
50
|
+
|
48
51
|
raise LanguageModelBadResponseError(message=msg, response_json=data)
|
49
52
|
if isinstance(current_data, str):
|
50
53
|
return current_data.strip()
|
@@ -55,17 +58,127 @@ def _extract_item_from_raw_response(data, sequence):
|
|
55
58
|
class RawResponseHandler:
|
56
59
|
"""Class to handle raw responses from language models."""
|
57
60
|
|
58
|
-
def __init__(self, key_sequence: list, usage_sequence: Optional[list] = None):
|
61
|
+
def __init__(self, key_sequence: list, usage_sequence: Optional[list] = None, reasoning_sequence: Optional[list] = None):
|
59
62
|
self.key_sequence = key_sequence
|
60
63
|
self.usage_sequence = usage_sequence
|
64
|
+
self.reasoning_sequence = reasoning_sequence
|
61
65
|
|
62
66
|
def get_generated_token_string(self, raw_response):
|
63
|
-
|
67
|
+
try:
|
68
|
+
return _extract_item_from_raw_response(raw_response, self.key_sequence)
|
69
|
+
except (LanguageModelKeyError, LanguageModelIndexError, LanguageModelTypeError, LanguageModelBadResponseError) as e:
|
70
|
+
# For non-reasoning models or reasoning models with different response formats,
|
71
|
+
# try to extract text directly from common response formats
|
72
|
+
if isinstance(raw_response, dict):
|
73
|
+
# Responses API format for non-reasoning models
|
74
|
+
if 'output' in raw_response and isinstance(raw_response['output'], list):
|
75
|
+
# Try to get first message content
|
76
|
+
if len(raw_response['output']) > 0:
|
77
|
+
item = raw_response['output'][0]
|
78
|
+
if isinstance(item, dict) and 'content' in item:
|
79
|
+
if isinstance(item['content'], list) and len(item['content']) > 0:
|
80
|
+
first_content = item['content'][0]
|
81
|
+
if isinstance(first_content, dict) and 'text' in first_content:
|
82
|
+
return first_content['text']
|
83
|
+
elif isinstance(item['content'], str):
|
84
|
+
return item['content']
|
85
|
+
|
86
|
+
# OpenAI completions format
|
87
|
+
if 'choices' in raw_response and isinstance(raw_response['choices'], list) and len(raw_response['choices']) > 0:
|
88
|
+
choice = raw_response['choices'][0]
|
89
|
+
if isinstance(choice, dict):
|
90
|
+
if 'text' in choice:
|
91
|
+
return choice['text']
|
92
|
+
elif 'message' in choice and isinstance(choice['message'], dict) and 'content' in choice['message']:
|
93
|
+
return choice['message']['content']
|
94
|
+
|
95
|
+
# Text directly in response
|
96
|
+
if 'text' in raw_response:
|
97
|
+
return raw_response['text']
|
98
|
+
elif 'content' in raw_response:
|
99
|
+
return raw_response['content']
|
100
|
+
|
101
|
+
# Error message - try to return a coherent error for debugging
|
102
|
+
if 'message' in raw_response:
|
103
|
+
return f"[ERROR: {raw_response['message']}]"
|
104
|
+
|
105
|
+
# If we get a string directly, return it
|
106
|
+
if isinstance(raw_response, str):
|
107
|
+
return raw_response
|
108
|
+
|
109
|
+
# As a last resort, convert the whole response to string
|
110
|
+
try:
|
111
|
+
return f"[ERROR: Could not extract text. Raw response: {str(raw_response)}]"
|
112
|
+
except:
|
113
|
+
return "[ERROR: Could not extract text from response]"
|
64
114
|
|
65
115
|
def get_usage_dict(self, raw_response):
|
66
116
|
if self.usage_sequence is None:
|
67
117
|
return {}
|
68
|
-
|
118
|
+
try:
|
119
|
+
return _extract_item_from_raw_response(raw_response, self.usage_sequence)
|
120
|
+
except (LanguageModelKeyError, LanguageModelIndexError, LanguageModelTypeError, LanguageModelBadResponseError):
|
121
|
+
# For non-reasoning models, try to extract usage from common response formats
|
122
|
+
if isinstance(raw_response, dict):
|
123
|
+
# Standard OpenAI usage format
|
124
|
+
if 'usage' in raw_response:
|
125
|
+
return raw_response['usage']
|
126
|
+
|
127
|
+
# Look for nested usage info
|
128
|
+
if 'choices' in raw_response and len(raw_response['choices']) > 0:
|
129
|
+
choice = raw_response['choices'][0]
|
130
|
+
if isinstance(choice, dict) and 'usage' in choice:
|
131
|
+
return choice['usage']
|
132
|
+
|
133
|
+
# If no usage info found, return empty dict
|
134
|
+
return {}
|
135
|
+
|
136
|
+
def get_reasoning_summary(self, raw_response):
|
137
|
+
"""
|
138
|
+
Extract reasoning summary from the model response.
|
139
|
+
|
140
|
+
Handles various response structures:
|
141
|
+
1. Standard path extraction using self.reasoning_sequence
|
142
|
+
2. Direct access to output[0]['summary'] for OpenAI responses
|
143
|
+
3. List responses where the first item contains the output structure
|
144
|
+
"""
|
145
|
+
if self.reasoning_sequence is None:
|
146
|
+
return None
|
147
|
+
|
148
|
+
try:
|
149
|
+
# First try the standard extraction path
|
150
|
+
summary_data = _extract_item_from_raw_response(raw_response, self.reasoning_sequence)
|
151
|
+
|
152
|
+
# If summary_data is a list of dictionaries with 'text' and 'type' fields
|
153
|
+
# (as in OpenAI's response format), combine them into a single string
|
154
|
+
if isinstance(summary_data, list) and all(isinstance(item, dict) and 'text' in item for item in summary_data):
|
155
|
+
return '\n\n'.join(item['text'] for item in summary_data)
|
156
|
+
|
157
|
+
return summary_data
|
158
|
+
except Exception:
|
159
|
+
# Fallback approaches for different response structures
|
160
|
+
try:
|
161
|
+
# Case 1: Direct dict with 'output' field (common OpenAI format)
|
162
|
+
if isinstance(raw_response, dict) and 'output' in raw_response:
|
163
|
+
output = raw_response['output']
|
164
|
+
if isinstance(output, list) and len(output) > 0 and 'summary' in output[0]:
|
165
|
+
summary_data = output[0]['summary']
|
166
|
+
if isinstance(summary_data, list) and all(isinstance(item, dict) and 'text' in item for item in summary_data):
|
167
|
+
return '\n\n'.join(item['text'] for item in summary_data)
|
168
|
+
|
169
|
+
# Case 2: List where the first item is a dict with 'output' field
|
170
|
+
if isinstance(raw_response, list) and len(raw_response) > 0:
|
171
|
+
first_item = raw_response[0]
|
172
|
+
if isinstance(first_item, dict) and 'output' in first_item:
|
173
|
+
output = first_item['output']
|
174
|
+
if isinstance(output, list) and len(output) > 0 and 'summary' in output[0]:
|
175
|
+
summary_data = output[0]['summary']
|
176
|
+
if isinstance(summary_data, list) and all(isinstance(item, dict) and 'text' in item for item in summary_data):
|
177
|
+
return '\n\n'.join(item['text'] for item in summary_data)
|
178
|
+
except Exception:
|
179
|
+
pass
|
180
|
+
|
181
|
+
return None
|
69
182
|
|
70
183
|
def parse_response(self, raw_response: dict[str, Any]) -> Any:
|
71
184
|
"""Parses the API response and returns the response text."""
|
@@ -73,7 +186,11 @@ class RawResponseHandler:
|
|
73
186
|
from edsl.data_transfer_models import EDSLOutput
|
74
187
|
|
75
188
|
generated_token_string = self.get_generated_token_string(raw_response)
|
189
|
+
# Ensure generated_token_string is a string before using string methods
|
190
|
+
if not isinstance(generated_token_string, str):
|
191
|
+
generated_token_string = str(generated_token_string)
|
76
192
|
last_newline = generated_token_string.rfind("\n")
|
193
|
+
reasoning_summary = self.get_reasoning_summary(raw_response)
|
77
194
|
|
78
195
|
if last_newline == -1:
|
79
196
|
# There is no comment
|
@@ -81,12 +198,14 @@ class RawResponseHandler:
|
|
81
198
|
"answer": self.convert_answer(generated_token_string),
|
82
199
|
"generated_tokens": generated_token_string,
|
83
200
|
"comment": None,
|
201
|
+
"reasoning_summary": reasoning_summary,
|
84
202
|
}
|
85
203
|
else:
|
86
204
|
edsl_dict = {
|
87
205
|
"answer": self.convert_answer(generated_token_string[:last_newline]),
|
88
|
-
"comment": generated_token_string[last_newline + 1
|
206
|
+
"comment": generated_token_string[last_newline + 1:].strip(),
|
89
207
|
"generated_tokens": generated_token_string,
|
208
|
+
"reasoning_summary": reasoning_summary,
|
90
209
|
}
|
91
210
|
return EDSLOutput(**edsl_dict)
|
92
211
|
|
edsl/results/result.py
CHANGED
@@ -95,6 +95,7 @@ class Result(Base, UserDict):
|
|
95
95
|
question_to_attributes: Optional[dict[QuestionName, Any]] = None,
|
96
96
|
generated_tokens: Optional[dict] = None,
|
97
97
|
comments_dict: Optional[dict] = None,
|
98
|
+
reasoning_summaries_dict: Optional[dict] = None,
|
98
99
|
cache_used_dict: Optional[dict[QuestionName, bool]] = None,
|
99
100
|
indices: Optional[dict] = None,
|
100
101
|
cache_keys: Optional[dict[QuestionName, str]] = None,
|
@@ -112,6 +113,7 @@ class Result(Base, UserDict):
|
|
112
113
|
:param question_to_attributes: A dictionary of question attributes.
|
113
114
|
:param generated_tokens: A dictionary of generated tokens.
|
114
115
|
:param comments_dict: A dictionary of comments.
|
116
|
+
:param reasoning_summaries_dict: A dictionary of reasoning summaries.
|
115
117
|
:param cache_used_dict: A dictionary of cache usage.
|
116
118
|
:param indices: A dictionary of indices.
|
117
119
|
|
@@ -130,6 +132,7 @@ class Result(Base, UserDict):
|
|
130
132
|
"question_to_attributes": self.question_to_attributes,
|
131
133
|
"generated_tokens": generated_tokens or {},
|
132
134
|
"comments_dict": comments_dict or {},
|
135
|
+
"reasoning_summaries_dict": reasoning_summaries_dict or {},
|
133
136
|
"cache_used_dict": cache_used_dict or {},
|
134
137
|
"cache_keys": cache_keys or {},
|
135
138
|
}
|
@@ -236,6 +239,7 @@ class Result(Base, UserDict):
|
|
236
239
|
"answer": self.data["answer"],
|
237
240
|
"prompt": self.data["prompt"],
|
238
241
|
"comment": self.data["comments_dict"],
|
242
|
+
"reasoning_summary": self.data["reasoning_summaries_dict"],
|
239
243
|
"generated_tokens": self.data["generated_tokens"],
|
240
244
|
"raw_model_response": self.data["raw_model_response"],
|
241
245
|
"question_text": sub_dicts_needing_new_keys["question_text"],
|
@@ -497,6 +501,7 @@ class Result(Base, UserDict):
|
|
497
501
|
question_to_attributes=json_dict.get("question_to_attributes", None),
|
498
502
|
generated_tokens=json_dict.get("generated_tokens", {}),
|
499
503
|
comments_dict=json_dict.get("comments_dict", {}),
|
504
|
+
reasoning_summaries_dict=json_dict.get("reasoning_summaries_dict", {}),
|
500
505
|
cache_used_dict=json_dict.get("cache_used_dict", {}),
|
501
506
|
cache_keys=json_dict.get("cache_keys", {}),
|
502
507
|
indices=json_dict.get("indices", None),
|
@@ -631,6 +636,36 @@ class Result(Base, UserDict):
|
|
631
636
|
}
|
632
637
|
return comments_dict
|
633
638
|
|
639
|
+
def get_reasoning_summaries_dict(answer_key_names) -> dict[str, Any]:
|
640
|
+
reasoning_summaries_dict = {}
|
641
|
+
for k in answer_key_names:
|
642
|
+
reasoning_summary = question_results[k].reasoning_summary
|
643
|
+
|
644
|
+
# If reasoning summary is None but we have a raw model response, try to extract it
|
645
|
+
if reasoning_summary is None and hasattr(question_results[k], 'raw_model_response'):
|
646
|
+
try:
|
647
|
+
# Get the model class to access the reasoning_sequence
|
648
|
+
model_class = interview.model.__class__ if hasattr(interview, 'model') else None
|
649
|
+
|
650
|
+
if model_class and hasattr(model_class, 'reasoning_sequence'):
|
651
|
+
from ..language_models.raw_response_handler import RawResponseHandler
|
652
|
+
|
653
|
+
# Create a handler with the model's reasoning sequence
|
654
|
+
handler = RawResponseHandler(
|
655
|
+
key_sequence=model_class.key_sequence if hasattr(model_class, 'key_sequence') else None,
|
656
|
+
usage_sequence=model_class.usage_sequence if hasattr(model_class, 'usage_sequence') else None,
|
657
|
+
reasoning_sequence=model_class.reasoning_sequence
|
658
|
+
)
|
659
|
+
|
660
|
+
# Try to extract the reasoning summary
|
661
|
+
reasoning_summary = handler.get_reasoning_summary(question_results[k].raw_model_response)
|
662
|
+
except Exception:
|
663
|
+
# If extraction fails, keep it as None
|
664
|
+
pass
|
665
|
+
|
666
|
+
reasoning_summaries_dict[k + "_reasoning_summary"] = reasoning_summary
|
667
|
+
return reasoning_summaries_dict
|
668
|
+
|
634
669
|
def get_question_name_to_prompts(
|
635
670
|
model_response_objects,
|
636
671
|
) -> dict[str, dict[str, str]]:
|
@@ -705,6 +740,7 @@ class Result(Base, UserDict):
|
|
705
740
|
answer_key_names = list(question_results.keys())
|
706
741
|
generated_tokens_dict = get_generated_tokens_dict(answer_key_names) if answer_key_names else {}
|
707
742
|
comments_dict = get_comments_dict(answer_key_names) if answer_key_names else {}
|
743
|
+
reasoning_summaries_dict = get_reasoning_summaries_dict(answer_key_names) if answer_key_names else {}
|
708
744
|
|
709
745
|
# Get answers that are in the question results
|
710
746
|
answer_dict = {}
|
@@ -735,6 +771,7 @@ class Result(Base, UserDict):
|
|
735
771
|
survey=survey_copy,
|
736
772
|
generated_tokens=generated_tokens_dict,
|
737
773
|
comments_dict=comments_dict,
|
774
|
+
reasoning_summaries_dict=reasoning_summaries_dict,
|
738
775
|
cache_used_dict=cache_used_dictionary,
|
739
776
|
indices=indices_copy,
|
740
777
|
cache_keys=cache_keys,
|
edsl/results/results.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edsl
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.60
|
4
4
|
Summary: Create and analyze LLM-based surveys
|
5
5
|
Home-page: https://www.expectedparrot.com/
|
6
6
|
License: MIT
|
@@ -43,7 +43,7 @@ Requires-Dist: pydot (>=2.0.0,<3.0.0)
|
|
43
43
|
Requires-Dist: pygments (>=2.17.2,<3.0.0)
|
44
44
|
Requires-Dist: pymupdf (>=1.25.5,<2.0.0)
|
45
45
|
Requires-Dist: pypdf2 (>=3.0.1,<4.0.0)
|
46
|
-
Requires-Dist: pyreadstat (
|
46
|
+
Requires-Dist: pyreadstat (==1.2.8)
|
47
47
|
Requires-Dist: python-docx (>=1.1.0,<2.0.0)
|
48
48
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
49
49
|
Requires-Dist: python-pptx (>=1.0.2,<2.0.0)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
edsl/__init__.py,sha256=EkpMsEKqKRbN9Qqcn_y8CjX8OjlWFyhxslLrt3SJY0Q,4827
|
2
2
|
edsl/__init__original.py,sha256=PzMzANf98PrSleSThXT4anNkeVqZMdw0tfFonzsoiGk,4446
|
3
|
-
edsl/__version__.py,sha256=
|
3
|
+
edsl/__version__.py,sha256=SH6dBtwKkjChke7DXHi0Y5HbtzV16-5wAfhv91d_D0A,23
|
4
4
|
edsl/agents/__init__.py,sha256=AyhfXjygRHT1Pd9w16lcu5Bu0jnBmMPz86aKP1uRL3Y,93
|
5
5
|
edsl/agents/agent.py,sha256=omq3lnEujOObKuDyr0seaTiRL7SbJxMjF6bZXqiTt7c,56296
|
6
6
|
edsl/agents/agent_list.py,sha256=k29SMOP2trdYWJs5-tPIfpme97fcnanL1lDhhJK3zfg,24249
|
@@ -9,8 +9,8 @@ edsl/agents/exceptions.py,sha256=7KMAtAHKqlkVkd_iVZC_mWXQnzDPV0V_n2iXaGAQgzc,566
|
|
9
9
|
edsl/base/__init__.py,sha256=h119NxrAJOV92jnX7ussXNjKFXqzySVGOjMG3G7Zkzc,992
|
10
10
|
edsl/base/base_class.py,sha256=bpuKCf6OOl71OlhrInDLC4b8LxFfDnuMVaaEaSp7ECY,48158
|
11
11
|
edsl/base/base_exception.py,sha256=gwk4mNoS3TBe6446NiQeSrUrjUqjlB3_fcDFgV90Dms,7644
|
12
|
-
edsl/base/data_transfer_models.py,sha256=
|
13
|
-
edsl/base/enums.py,sha256=
|
12
|
+
edsl/base/data_transfer_models.py,sha256=JpEnlgdQ5_URixzZUr7MJuAY4U6obPo0rWfzDl39WNg,3934
|
13
|
+
edsl/base/enums.py,sha256=46mqtWjeiL6NTsN8j-zGfY8QNOVXO4sVb1p1MjmD1N4,6613
|
14
14
|
edsl/base/exceptions.py,sha256=hEMu40lW1IsuarZiOJAL2sAUwuxsubxfR41J6BK5Ri8,3493
|
15
15
|
edsl/base.py,sha256=9Jx5zXfWLtKAm0L7LD_kTF3rSIR-tlEuCEuXDbeqHxI,221
|
16
16
|
edsl/buckets/__init__.py,sha256=g3VzxuhrC4wO1i6sljXAcJ_k6MNAu_OH-wAmSfzxBjI,1536
|
@@ -52,7 +52,7 @@ edsl/coop/utils.py,sha256=DON2ns5nWlUqqvlNVUsdgiPlz-6oEqFVOmjhnOwHQBs,8174
|
|
52
52
|
edsl/data_transfer_models.py,sha256=pPaKsbo9pgNcBB9kX-U2O_dUtNkd0Xm4JNmv26jrbhI,265
|
53
53
|
edsl/dataset/__init__.py,sha256=RIzfFIytKJfniKZ0VThMk8Z2fjejx91t9PZBct78xXw,422
|
54
54
|
edsl/dataset/dataset.py,sha256=o1icaFSE2ipCj7FDqhXkPb-E42wBzn74hLD7QXg0qaE,42277
|
55
|
-
edsl/dataset/dataset_operations_mixin.py,sha256=
|
55
|
+
edsl/dataset/dataset_operations_mixin.py,sha256=k0t4MF_nOIf7McLV6JNLUkGmQNoeDvP5kN0Z_aK9JHA,59344
|
56
56
|
edsl/dataset/dataset_tree.py,sha256=mKLQhwo-gxDyJCwCH3gj6Os0Jk2JqfWd_PvUyuWqM6s,14268
|
57
57
|
edsl/dataset/display/CSSParameterizer.py,sha256=vI3VTgTihJeCYGfmGp7fOhTitHZ17jrDGbq46Sa2rd8,3677
|
58
58
|
edsl/dataset/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -81,7 +81,7 @@ edsl/inference_services/models_available_cache.py,sha256=bOvevfRn2HlmBcHalaDkjFL
|
|
81
81
|
edsl/inference_services/rate_limits_cache.py,sha256=HYslviz7mxF9U4CUTPAkoyBsiXjSju-YCp4HHir6e34,1398
|
82
82
|
edsl/inference_services/registry.py,sha256=KIs1GpGSrczqukm6QsKe9pPn9LnfSrpT_wVAthU3-HM,307
|
83
83
|
edsl/inference_services/service_availability.py,sha256=z4rkonyD51Y-flKoxrpxBZIfOqjNrOXCxWCmA0mlUDU,4176
|
84
|
-
edsl/inference_services/services/__init__.py,sha256=
|
84
|
+
edsl/inference_services/services/__init__.py,sha256=_6F-qINiAj9spBmCRKgQR9tYDEek3x1uzX9CKX-3DLw,1010
|
85
85
|
edsl/inference_services/services/anthropic_service.py,sha256=yeSdXbqZ0RxXd2NKOrgXM4V5K2ioV_W1h-qujsPQpRU,4212
|
86
86
|
edsl/inference_services/services/aws_bedrock.py,sha256=tijqEp4IgKWeJiqR90T3zkeXaqA0Qat9h5eyhG1qY3M,4285
|
87
87
|
edsl/inference_services/services/azure_ai.py,sha256=7tZzyOhvT0eRooO2hccTZNghykth1e05MWlUKZNovpo,9436
|
@@ -92,6 +92,7 @@ edsl/inference_services/services/groq_service.py,sha256=eSxVbQXzrc6rtgVyMgDOsdG0
|
|
92
92
|
edsl/inference_services/services/mistral_ai_service.py,sha256=tvwIeqhwzT6kPjrUo_lO3QCSBYUGD7jHG010FPp72Z4,3925
|
93
93
|
edsl/inference_services/services/ollama_service.py,sha256=quSKlgD0bHG9mO_s9verGePfqQi_rZWovHEQ6dy-Fe0,303
|
94
94
|
edsl/inference_services/services/open_ai_service.py,sha256=WFcl9g7Y28hckdiD_bPxRL_yJqz9ukERL3h_znh6b80,8682
|
95
|
+
edsl/inference_services/services/open_ai_service_v2.py,sha256=KywwuZKeJA3zmnVU8EQC32xt0ICVPDp2suKpeAPxWAg,9027
|
95
96
|
edsl/inference_services/services/perplexity_service.py,sha256=7bt5Mb6Dxkb7UOljNdTBpZuT_8ri4i6Sk_h5g8paKu4,5994
|
96
97
|
edsl/inference_services/services/test_service.py,sha256=JUK2bch1uu5XefMhNnuAXCbTqgiMqQRAIN8xYCMNe1E,7394
|
97
98
|
edsl/inference_services/services/together_ai_service.py,sha256=biUYs07jsrIHp19O81o0nJCwYdSWudMEXdGtmA1-y60,6151
|
@@ -127,7 +128,7 @@ edsl/invigilators/question_template_replacements_builder.py,sha256=a_-n0TWE4PLK_
|
|
127
128
|
edsl/jobs/__init__.py,sha256=gBGDlPZiaTkKENGdGYaMKzk0BFf5R1Cv9yk2YMPvIqI,1183
|
128
129
|
edsl/jobs/async_interview_runner.py,sha256=rj07EKRu4fjbBkTADn8RAxbMF6m3vZFOG1qtnn0g12U,9532
|
129
130
|
edsl/jobs/check_survey_scenario_compatibility.py,sha256=9qD9qi6qjvC-4M3Mq2bSF8F5HMIbWilSVPSJ3wlFqmM,4022
|
130
|
-
edsl/jobs/data_structures.py,sha256=
|
131
|
+
edsl/jobs/data_structures.py,sha256=jQPl4KIv4WZ7rJ9bQD2_S14T_oyG7mN9nPiA2eqrYMo,10020
|
131
132
|
edsl/jobs/decorators.py,sha256=0Eot9pFPsWmQIJAafNd0f5hdb9RUAFp_hGMmSUTJ_C8,3272
|
132
133
|
edsl/jobs/exceptions.py,sha256=5lktTya2VgiBR5Bd977tG2xHdrMjDqhPhQO17O6jIdc,7220
|
133
134
|
edsl/jobs/fetch_invigilator.py,sha256=nzXAIulvOvuDpRDEN5TDNmEfikUEwrnS_XCtnYG2uPQ,2795
|
@@ -146,16 +147,16 @@ edsl/jobs/results_exceptions_handler.py,sha256=VCtnd60xwdFznzGhtXPbxLmyVf3kIjR24
|
|
146
147
|
edsl/key_management/__init__.py,sha256=JiOJ71Ly9aw-tVYbWZu-qRjsW4QETYMQ9IJjsKgW1DQ,1274
|
147
148
|
edsl/key_management/exceptions.py,sha256=dDtoDh1UL52BUBrAlCIc_McgtZCAQkUx6onoSz26qeM,2158
|
148
149
|
edsl/key_management/key_lookup.py,sha256=HfIntc_i_WWUDoMOLwAHHbNlwC-0HivOyf_djeKiPlo,6080
|
149
|
-
edsl/key_management/key_lookup_builder.py,sha256=
|
150
|
+
edsl/key_management/key_lookup_builder.py,sha256=s5H_DBGZpMJwaQc1fLh46GYfTpSUOOOsl_gsVCVkkKg,16050
|
150
151
|
edsl/key_management/key_lookup_collection.py,sha256=b1STYU4FIqgCtCf90bRZh6IXf8kcoTC8ad8RSHPmw-w,3471
|
151
152
|
edsl/key_management/models.py,sha256=z9TimNMnz47mnITM5SlJy2m2sk1aKKtt0ybV89rsaiY,6703
|
152
153
|
edsl/language_models/__init__.py,sha256=WtefJs6XOCn5RSz22PgoAi3eTEr1NzGtnnBpDIie2mg,240
|
153
154
|
edsl/language_models/exceptions.py,sha256=P9dMA8XfK_qcuXNJZ-Xsb_Ny-12Ldu3fPC133RB40Ek,13728
|
154
|
-
edsl/language_models/language_model.py,sha256=
|
155
|
+
edsl/language_models/language_model.py,sha256=e1RZLnLin3haLUYqfu5aQ0pLhopqqQuiQjxC2pVTW9E,46582
|
155
156
|
edsl/language_models/model.py,sha256=oYZsfgvko_EH4EWT9XZPEgLcs9KA36SGEAKZwYRFjv8,12013
|
156
157
|
edsl/language_models/model_list.py,sha256=Eb62xQdrlayqWYyJVgYxheMiNi14e1U9b_12qYzy1ws,4522
|
157
158
|
edsl/language_models/price_manager.py,sha256=74XEkoVdQv06w7gMFZmXeeXGW6om4_ISr-qFnmX4lFE,10711
|
158
|
-
edsl/language_models/raw_response_handler.py,sha256=
|
159
|
+
edsl/language_models/raw_response_handler.py,sha256=WynUO2q986ALb9QJ2IS6erAiDWFy8_Zr2lUAMdGWkaY,10708
|
159
160
|
edsl/language_models/registry.py,sha256=io_Cp-7PtLpPuvZs_j8XaMxJiv-zSplbAQdrzPp2pzg,7308
|
160
161
|
edsl/language_models/repair.py,sha256=ljm0xc9e1tMdyKc9b-v7ikpYRBh639xJ11SkDzI2vZE,5245
|
161
162
|
edsl/language_models/unused/fake_openai_call.py,sha256=dxbL5e4NLF-eTk9IduPyGwLiVCX_-eGCJDaLYPlQTqc,364
|
@@ -278,8 +279,8 @@ edsl/questions/validation_logger.py,sha256=ru0y2uM3t9Hln2oaq-n-9d4zTKXQIQWiKincG
|
|
278
279
|
edsl/results/__init__.py,sha256=RKbHY0g6s_k42VcdmTOZ2yB_nltiJnnbeQAkUY5WD9o,129
|
279
280
|
edsl/results/exceptions.py,sha256=u-TQsazt_qj-G4eJKBnj0UtpnIiw6A2GcCLJ2wTYE_g,6536
|
280
281
|
edsl/results/report.py,sha256=oHjMY981Gn8estqvoTk5SPiuEOIM0IR_QPBrRLdk5pM,7481
|
281
|
-
edsl/results/result.py,sha256=
|
282
|
-
edsl/results/results.py,sha256=
|
282
|
+
edsl/results/result.py,sha256=SZekHBstRMhuvhz20cPaTREY7Rq4tQIF0Nc6tyWWyjE,32160
|
283
|
+
edsl/results/results.py,sha256=dd0MvTU0Rg4mLoyryKv8mreVmM1Eu6UPGPDJic16P_E,85002
|
283
284
|
edsl/results/results_selector.py,sha256=4_XMS2Fb-3rcXEPUYaBRw52r1i66jttjttqNFe6PRc4,18050
|
284
285
|
edsl/scenarios/DocxScenario.py,sha256=ul3nkX826m_T6LFptswqtnH5czP_yxMlLWgbTmFIZI4,482
|
285
286
|
edsl/scenarios/PdfExtractor.py,sha256=6nPZ6v9x2RrU42EkqlEcW3MS-WIQpGfwg4--6WvEC8I,1972
|
@@ -382,8 +383,8 @@ edsl/utilities/restricted_python.py,sha256=248N2p5EWHDSpcK1G-q7DUoJeWy4sB6aO-RV0
|
|
382
383
|
edsl/utilities/template_loader.py,sha256=SCAcnTnxNQ67MNSkmfz7F-S_u2peyGn2j1oRIqi1wfg,870
|
383
384
|
edsl/utilities/utilities.py,sha256=irHheAGOnl_6RwI--Hi9StVzvsHcWCqB48PWsWJQYOw,12045
|
384
385
|
edsl/utilities/wikipedia.py,sha256=I3Imbz3fzbaoA0ZLDsWUO2YpP_ovvaqtu-yd2Ye1BB0,6933
|
385
|
-
edsl-0.1.
|
386
|
-
edsl-0.1.
|
387
|
-
edsl-0.1.
|
388
|
-
edsl-0.1.
|
389
|
-
edsl-0.1.
|
386
|
+
edsl-0.1.60.dist-info/LICENSE,sha256=_qszBDs8KHShVYcYzdMz3HNMtH-fKN_p5zjoVAVumFc,1111
|
387
|
+
edsl-0.1.60.dist-info/METADATA,sha256=KodgK6MWkw8_QG3LVrIJFqbcQI3oMUowIfcrvdggkBU,12075
|
388
|
+
edsl-0.1.60.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
389
|
+
edsl-0.1.60.dist-info/entry_points.txt,sha256=JnG7xqMtHaQu9BU-yPATxdyCeA48XJpuclnWCqMfIMU,38
|
390
|
+
edsl-0.1.60.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|