edsl 0.1.36.dev1__py3-none-any.whl → 0.1.36.dev3__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.36.dev1"
1
+ __version__ = "0.1.36.dev3"
edsl/coop/PriceFetcher.py CHANGED
@@ -16,30 +16,26 @@ class PriceFetcher:
16
16
  if self._cached_prices is not None:
17
17
  return self._cached_prices
18
18
 
19
+ import os
19
20
  import requests
20
- import csv
21
- from io import StringIO
22
-
23
- sheet_id = "1SAO3Bhntefl0XQHJv27rMxpvu6uzKDWNXFHRa7jrUDs"
24
-
25
- # Construct the URL to fetch the CSV
26
- url = f"https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=csv"
21
+ from edsl import CONFIG
27
22
 
28
23
  try:
29
- # Fetch the CSV data
30
- response = requests.get(url)
24
+ # Fetch the pricing data
25
+ url = f"{CONFIG.EXPECTED_PARROT_URL}/api/v0/prices"
26
+ api_key = os.getenv("EXPECTED_PARROT_API_KEY")
27
+ headers = {}
28
+ if api_key:
29
+ headers["Authorization"] = f"Bearer {api_key}"
30
+ else:
31
+ headers["Authorization"] = f"Bearer None"
32
+
33
+ response = requests.get(url, headers=headers, timeout=20)
31
34
  response.raise_for_status() # Raise an exception for bad responses
32
35
 
33
- # Parse the CSV data
34
- csv_data = StringIO(response.text)
35
- reader = csv.reader(csv_data)
36
-
37
- # Convert to list of dictionaries
38
- headers = next(reader)
39
- data = [dict(zip(headers, row)) for row in reader]
36
+ # Parse the data
37
+ data = response.json()
40
38
 
41
- # self._cached_prices = data
42
- # return data
43
39
  price_lookup = {}
44
40
  for entry in data:
45
41
  service = entry.get("service", None)
edsl/coop/coop.py CHANGED
@@ -584,7 +584,10 @@ class Coop:
584
584
  )
585
585
  self._resolve_server_response(response)
586
586
  response_json = response.json()
587
- return response_json.get("cost")
587
+ return {
588
+ "credits": response_json.get("cost_in_credits"),
589
+ "usd": response_json.get("cost_in_usd"),
590
+ }
588
591
 
589
592
  ################
590
593
  # Remote Errors
@@ -11,21 +11,29 @@ from edsl.inference_services.AwsBedrock import AwsBedrockService
11
11
  from edsl.inference_services.AzureAI import AzureAIService
12
12
  from edsl.inference_services.OllamaService import OllamaService
13
13
  from edsl.inference_services.TestService import TestService
14
- from edsl.inference_services.MistralAIService import MistralAIService
15
14
  from edsl.inference_services.TogetherAIService import TogetherAIService
16
15
 
17
- default = InferenceServicesCollection(
18
- [
19
- OpenAIService,
20
- AnthropicService,
21
- DeepInfraService,
22
- GoogleService,
23
- GroqService,
24
- AwsBedrockService,
25
- AzureAIService,
26
- OllamaService,
27
- TestService,
28
- MistralAIService,
29
- TogetherAIService,
30
- ]
31
- )
16
+ try:
17
+ from edsl.inference_services.MistralAIService import MistralAIService
18
+
19
+ mistral_available = True
20
+ except Exception as e:
21
+ mistral_available = False
22
+
23
+ services = [
24
+ OpenAIService,
25
+ AnthropicService,
26
+ DeepInfraService,
27
+ GoogleService,
28
+ GroqService,
29
+ AwsBedrockService,
30
+ AzureAIService,
31
+ OllamaService,
32
+ TestService,
33
+ TogetherAIService,
34
+ ]
35
+
36
+ if mistral_available:
37
+ services.append(MistralAIService)
38
+
39
+ default = InferenceServicesCollection(services)
edsl/jobs/Jobs.py CHANGED
@@ -219,11 +219,11 @@ class Jobs(Base):
219
219
  price_lookup: dict,
220
220
  inference_service: str,
221
221
  model: str,
222
- ):
222
+ ) -> dict:
223
223
  """Estimates the cost of a prompt. Takes piping into account."""
224
224
 
225
225
  def get_piping_multiplier(prompt: str):
226
- """Returns 2 if a prompt includes Jinja brances, and 1 otherwise."""
226
+ """Returns 2 if a prompt includes Jinja braces, and 1 otherwise."""
227
227
 
228
228
  if "{{" in prompt and "}}" in prompt:
229
229
  return 2
@@ -231,9 +231,25 @@ class Jobs(Base):
231
231
 
232
232
  # Look up prices per token
233
233
  key = (inference_service, model)
234
- relevant_prices = price_lookup[key]
235
- output_price_per_token = 1 / float(relevant_prices["output"]["one_usd_buys"])
236
- input_price_per_token = 1 / float(relevant_prices["input"]["one_usd_buys"])
234
+
235
+ try:
236
+ relevant_prices = price_lookup[key]
237
+ output_price_per_token = 1 / float(
238
+ relevant_prices["output"]["one_usd_buys"]
239
+ )
240
+ input_price_per_token = 1 / float(relevant_prices["input"]["one_usd_buys"])
241
+ except KeyError:
242
+ # A KeyError is likely to occur if we cannot retrieve prices (the price_lookup dict is empty)
243
+ # Use a sensible default
244
+
245
+ import warnings
246
+
247
+ warnings.warn(
248
+ "Price data could not be retrieved. Using default estimates for input and output token prices. Input: $0.15 / 1M tokens; Output: $0.60 / 1M tokens"
249
+ )
250
+
251
+ output_price_per_token = 0.00000015 # $0.15 / 1M tokens
252
+ input_price_per_token = 0.00000060 # $0.60 / 1M tokens
237
253
 
238
254
  # Compute the number of characters (double if the question involves piping)
239
255
  user_prompt_chars = len(str(user_prompt)) * get_piping_multiplier(
@@ -258,7 +274,7 @@ class Jobs(Base):
258
274
  "cost": cost,
259
275
  }
260
276
 
261
- def estimate_job_cost_from_external_prices(self, price_lookup: dict):
277
+ def estimate_job_cost_from_external_prices(self, price_lookup: dict) -> dict:
262
278
  """
263
279
  Estimates the cost of a job according to the following assumptions:
264
280
 
@@ -341,7 +357,7 @@ class Jobs(Base):
341
357
 
342
358
  return output
343
359
 
344
- def estimate_job_cost(self):
360
+ def estimate_job_cost(self) -> dict:
345
361
  """
346
362
  Estimates the cost of a job according to the following assumptions:
347
363
 
@@ -357,6 +373,25 @@ class Jobs(Base):
357
373
 
358
374
  return self.estimate_job_cost_from_external_prices(price_lookup=price_lookup)
359
375
 
376
+ @staticmethod
377
+ def compute_job_cost(job_results: "Results") -> float:
378
+ """
379
+ Computes the cost of a completed job in USD.
380
+ """
381
+ total_cost = 0
382
+ for result in job_results:
383
+ for key in result.raw_model_response:
384
+ if key.endswith("_cost"):
385
+ result_cost = result.raw_model_response[key]
386
+
387
+ question_name = key.removesuffix("_cost")
388
+ cache_used = result.cache_used_dict[question_name]
389
+
390
+ if isinstance(result_cost, (int, float)) and not cache_used:
391
+ total_cost += result_cost
392
+
393
+ return total_cost
394
+
360
395
  @staticmethod
361
396
  def _get_container_class(object):
362
397
  from edsl.agents.AgentList import AgentList
@@ -173,19 +173,20 @@ class JobsRunnerAsyncio:
173
173
 
174
174
  prompt_dictionary = {}
175
175
  for answer_key_name in answer_key_names:
176
- prompt_dictionary[
177
- answer_key_name + "_user_prompt"
178
- ] = question_name_to_prompts[answer_key_name]["user_prompt"]
179
- prompt_dictionary[
180
- answer_key_name + "_system_prompt"
181
- ] = question_name_to_prompts[answer_key_name]["system_prompt"]
176
+ prompt_dictionary[answer_key_name + "_user_prompt"] = (
177
+ question_name_to_prompts[answer_key_name]["user_prompt"]
178
+ )
179
+ prompt_dictionary[answer_key_name + "_system_prompt"] = (
180
+ question_name_to_prompts[answer_key_name]["system_prompt"]
181
+ )
182
182
 
183
183
  raw_model_results_dictionary = {}
184
+ cache_used_dictionary = {}
184
185
  for result in valid_results:
185
186
  question_name = result.question_name
186
- raw_model_results_dictionary[
187
- question_name + "_raw_model_response"
188
- ] = result.raw_model_response
187
+ raw_model_results_dictionary[question_name + "_raw_model_response"] = (
188
+ result.raw_model_response
189
+ )
189
190
  raw_model_results_dictionary[question_name + "_cost"] = result.cost
190
191
  one_use_buys = (
191
192
  "NA"
@@ -195,6 +196,7 @@ class JobsRunnerAsyncio:
195
196
  else 1.0 / result.cost
196
197
  )
197
198
  raw_model_results_dictionary[question_name + "_one_usd_buys"] = one_use_buys
199
+ cache_used_dictionary[question_name] = result.cache_used
198
200
 
199
201
  result = Result(
200
202
  agent=interview.agent,
@@ -207,6 +209,7 @@ class JobsRunnerAsyncio:
207
209
  survey=interview.survey,
208
210
  generated_tokens=generated_tokens_dict,
209
211
  comments_dict=comments_dict,
212
+ cache_used_dict=cache_used_dictionary,
210
213
  )
211
214
  result.interview_hash = hash(interview)
212
215
 
edsl/results/Result.py CHANGED
@@ -75,6 +75,7 @@ class Result(Base, UserDict):
75
75
  question_to_attributes: Optional[dict] = None,
76
76
  generated_tokens: Optional[dict] = None,
77
77
  comments_dict: Optional[dict] = None,
78
+ cache_used_dict: Optional[dict] = None,
78
79
  ):
79
80
  """Initialize a Result object.
80
81
 
@@ -130,6 +131,7 @@ class Result(Base, UserDict):
130
131
  self.question_to_attributes = question_to_attributes
131
132
  self.generated_tokens = generated_tokens
132
133
  self.comments_dict = comments_dict or {}
134
+ self.cache_used_dict = cache_used_dict or {}
133
135
 
134
136
  self._combined_dict = None
135
137
  self._problem_keys = None
@@ -153,15 +155,15 @@ class Result(Base, UserDict):
153
155
  if key in self.question_to_attributes:
154
156
  # You might be tempted to just use the naked key
155
157
  # but this is a bad idea because it pollutes the namespace
156
- question_text_dict[
157
- key + "_question_text"
158
- ] = self.question_to_attributes[key]["question_text"]
159
- question_options_dict[
160
- key + "_question_options"
161
- ] = self.question_to_attributes[key]["question_options"]
162
- question_type_dict[
163
- key + "_question_type"
164
- ] = self.question_to_attributes[key]["question_type"]
158
+ question_text_dict[key + "_question_text"] = (
159
+ self.question_to_attributes[key]["question_text"]
160
+ )
161
+ question_options_dict[key + "_question_options"] = (
162
+ self.question_to_attributes[key]["question_options"]
163
+ )
164
+ question_type_dict[key + "_question_type"] = (
165
+ self.question_to_attributes[key]["question_type"]
166
+ )
165
167
 
166
168
  return {
167
169
  "agent": self.agent.traits
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edsl
3
- Version: 0.1.36.dev1
3
+ Version: 0.1.36.dev3
4
4
  Summary: Create and analyze LLM-based surveys
5
5
  Home-page: https://www.expectedparrot.com/
6
6
  License: MIT
@@ -2,7 +2,7 @@ edsl/Base.py,sha256=wdFpHWlQlGNL4XfOmYA0AK9YupMDxK3G7mDHCQp60o4,9295
2
2
  edsl/BaseDiff.py,sha256=RoVEh52UJs22yMa7k7jv8se01G62jJNWnBzaZngo-Ug,8260
3
3
  edsl/TemplateLoader.py,sha256=sDBlSMt7EfOduM7w3h6v03gvh_Rzn9hVrlS-iLSQdZA,849
4
4
  edsl/__init__.py,sha256=UZcx9RHSi3Dslh2lWvCOeppdMW9Xzw_YLs-kFaNW1MU,1770
5
- edsl/__version__.py,sha256=yS7SjTPwUGI_RFJspMUKx7G1-n8hVfU0zsPUxn0IprE,28
5
+ edsl/__version__.py,sha256=O6OeyPKUHgURPf_T_wkVQhQyXsqbu_Q16mVBZIJ86uM,28
6
6
  edsl/agents/Agent.py,sha256=dG3SbCm4IpHpObcWm-OejfYHtVXa5NlxGKYKOc-dUxQ,29311
7
7
  edsl/agents/AgentList.py,sha256=qo8VK3Ov0YOSbsBcHmlwLZBH81CcDfy5IEcx9AVH78M,10963
8
8
  edsl/agents/Invigilator.py,sha256=m4T-z4aNCGd4LKjLXVNI2VszYW-pQeScfcFAxkb0pWc,9080
@@ -44,9 +44,9 @@ edsl/conversation/Conversation.py,sha256=NdWH62XpcF6hoaG0ScMho_c3TO7PfBnbdlppUN-
44
44
  edsl/conversation/car_buying.py,sha256=Quh2Q8O9YoCyTKJUy3li376QFIOcL1gX0y89w3wlSl4,1950
45
45
  edsl/conversation/mug_negotiation.py,sha256=mjvAqErD4AjN3G2za2c-X-3axOShW-zAJUeiJqTxVPA,2616
46
46
  edsl/conversation/next_speaker_utilities.py,sha256=bqr5JglCd6bdLc9IZ5zGOAsmN2F4ERiubSMYvZIG7qk,3629
47
- edsl/coop/PriceFetcher.py,sha256=7q8r1FqE9ap1CMi6WiE_pZQhYxEqG9_tgPgGxLQYVX8,1894
47
+ edsl/coop/PriceFetcher.py,sha256=pCCWBqFnSv8iYpgQKhAzVCdan1xTCNesZgmIB34N4HY,1770
48
48
  edsl/coop/__init__.py,sha256=4iZCwJSzJVyjBYk8ggGxY2kZjq9dXVT1jhyPDNyew4I,115
49
- edsl/coop/coop.py,sha256=_wdT6Z-fFYGtAWLn1e98Vv8hgwhdJM2vH_2G-J2ugiI,28547
49
+ edsl/coop/coop.py,sha256=cN2lhj-pFucOB8KUoZRInu_65w3WBaXLcpg_zC6N-vM,28647
50
50
  edsl/coop/utils.py,sha256=UZwljKYW_Yjw7RYcjOg3SW7fn1pyHQfJ1fM48TBNoss,3601
51
51
  edsl/data/Cache.py,sha256=jDt0LoZjLpGnM8-CraQEcsQaVg--U3BiBR1zHj0nDn8,16536
52
52
  edsl/data/CacheEntry.py,sha256=_5UiFaJQu_U-Z1_lEPt-h6Gaidp2Eunk02wOd3Ni3MQ,7252
@@ -84,10 +84,10 @@ edsl/inference_services/TogetherAIService.py,sha256=p_31ccrfN25kZF2xlAlUkb7w1EL4
84
84
  edsl/inference_services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
85
  edsl/inference_services/models_available_cache.py,sha256=HtGNaYgrxY2oPy-QruKhjj6LUzhGNqBhLHFWMoMi1E4,3312
86
86
  edsl/inference_services/rate_limits_cache.py,sha256=HYslviz7mxF9U4CUTPAkoyBsiXjSju-YCp4HHir6e34,1398
87
- edsl/inference_services/registry.py,sha256=CwdaQ-A5PTb5lFKMQdOfl8IqCw2SVJ8HlC-_2uZf11k,1141
87
+ edsl/inference_services/registry.py,sha256=Fn6va65MqD9lnFvT603ZnU7Ok8IW64M2MzOH57kf9-A,1240
88
88
  edsl/inference_services/write_available.py,sha256=NNwhATlaMp8IYY635MSx-oYxt5X15acjAfaqYCo_I1Y,285
89
89
  edsl/jobs/Answers.py,sha256=c4LpigQjdnMr7iJu8571C4FggGPVudfT7hbJgmgKW40,1821
90
- edsl/jobs/Jobs.py,sha256=t37-_8PemRGfQABTyuXPYW53-4sevPWkvF6qeq8oLxc,40183
90
+ edsl/jobs/Jobs.py,sha256=CgZ3oVI4StzoQsjXp8K7k3cZbo_jf-DH0kAbn-LECxo,41466
91
91
  edsl/jobs/__init__.py,sha256=aKuAyd_GoalGj-k7djOoVwEbFUE2XLPlikXaA1_8yAg,32
92
92
  edsl/jobs/buckets/BucketCollection.py,sha256=11CRisE1WAPcAlI3YJK3DVvu0AqSvv8KskXo4Q1waSk,2286
93
93
  edsl/jobs/buckets/ModelBuckets.py,sha256=hxw_tzc0V42CiB7mh5jIxlgwDVJ-zFZhlLtKrHEg8ho,2419
@@ -101,7 +101,7 @@ edsl/jobs/interviews/InterviewStatusDictionary.py,sha256=MSyys4hOWe1d8gfsUvAPbcK
101
101
  edsl/jobs/interviews/InterviewStatusLog.py,sha256=6u0F8gf5tha39VQL-IK_QPkCsQAYVOx_IesX7TDDX_A,3252
102
102
  edsl/jobs/interviews/ReportErrors.py,sha256=RSzDU2rWwtjfztj7sqaMab0quCiY-X2bG3AEOxhTim8,1745
103
103
  edsl/jobs/interviews/interview_status_enum.py,sha256=KJ-1yLAHdX-p8TiFnM0M3v1tnBwkq4aMCuBX6-ytrI8,229
104
- edsl/jobs/runners/JobsRunnerAsyncio.py,sha256=SXWFTgTYQzqIaBjPQ2mRrxR3aGFMQyw2eD9wX8tZNkQ,12850
104
+ edsl/jobs/runners/JobsRunnerAsyncio.py,sha256=urlD6tRQ9iI2k23sod9C1tKFOM0Y7B9Mcp31VChcIB4,13011
105
105
  edsl/jobs/runners/JobsRunnerStatus.py,sha256=4eCh9sRpswGdKeSMW9pCGCAjJZa-OrWUPI7tsxIy_g4,12112
106
106
  edsl/jobs/runners/JobsRunnerStatusData.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
107
  edsl/jobs/tasks/QuestionTaskCreator.py,sha256=K-xATHIXMWPTMOnms5UDW30eTIlIfebf7oOEfwrh1ME,10072
@@ -202,7 +202,7 @@ edsl/questions/templates/yes_no/question_presentation.jinja,sha256=hoEVj4GQD3EYn
202
202
  edsl/results/Dataset.py,sha256=XeCWNcni1rde9iVzmC1WTIne2cip4-f2gQL5iaJfXNw,9202
203
203
  edsl/results/DatasetExportMixin.py,sha256=-YR-UeuIW_8u0a8HnQ9R6V41DxCq22_AlsD48fXv0sw,25890
204
204
  edsl/results/DatasetTree.py,sha256=nwEgnWBqRXUxagSCEgqwikmIo8ztUxaF-QH-m-8myyQ,4985
205
- edsl/results/Result.py,sha256=o3Tg07loDcwNTb6wPgV_qS7_REkwcMVouc9t_-zwkpw,15469
205
+ edsl/results/Result.py,sha256=85TlWtcNwCc98N-w3JF0APIkq5LmHfB8cXyW1T5s3f8,15576
206
206
  edsl/results/Results.py,sha256=zp4yDt-rPqgEPpv2xCGppQIzzwiKX-BbUpnJtY739L4,40807
207
207
  edsl/results/ResultsDBMixin.py,sha256=Hc08aOiArBf9jbxI5uV4VL4wT6BLOkaaEgTMb3zyTUI,7922
208
208
  edsl/results/ResultsExportMixin.py,sha256=XizBsPNxziyffirMA4kS7UHpYM1WIE4s1K-B7TqTfDw,1266
@@ -272,7 +272,7 @@ edsl/utilities/interface.py,sha256=AaKpWiwWBwP2swNXmnFlIf3ZFsjfsR5bjXQAW47tD-8,1
272
272
  edsl/utilities/repair_functions.py,sha256=tftmklAqam6LOQQu_-9U44N-llycffhW8LfO63vBmNw,929
273
273
  edsl/utilities/restricted_python.py,sha256=5-_zUhrNbos7pLhDl9nr8d24auRlquR6w-vKkmNjPiA,2060
274
274
  edsl/utilities/utilities.py,sha256=gqMtWWNEZkWLiRR9vHW-VRNy2bStEPlJ-I2aK9CwFiQ,11367
275
- edsl-0.1.36.dev1.dist-info/LICENSE,sha256=_qszBDs8KHShVYcYzdMz3HNMtH-fKN_p5zjoVAVumFc,1111
276
- edsl-0.1.36.dev1.dist-info/METADATA,sha256=dTcbX2Paflg5gnjssUQVafoVYWs2FDACLaSf2ngOAc8,4476
277
- edsl-0.1.36.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
278
- edsl-0.1.36.dev1.dist-info/RECORD,,
275
+ edsl-0.1.36.dev3.dist-info/LICENSE,sha256=_qszBDs8KHShVYcYzdMz3HNMtH-fKN_p5zjoVAVumFc,1111
276
+ edsl-0.1.36.dev3.dist-info/METADATA,sha256=wpLmlLTVi16NQ3gIq388xFmlvYOBnc_iR8zpOUDDiRI,4476
277
+ edsl-0.1.36.dev3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
278
+ edsl-0.1.36.dev3.dist-info/RECORD,,