edsl 0.1.46__py3-none-any.whl → 0.1.47__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 CHANGED
@@ -65,13 +65,11 @@ class PersistenceMixin:
65
65
  def pull(
66
66
  cls,
67
67
  url_or_uuid: Optional[Union[str, UUID]] = None,
68
- # expected_parrot_url: Optional[str] = None,
69
68
  ):
70
69
  """Pull the object from coop.
71
70
 
72
71
  Args:
73
72
  url_or_uuid: Either a UUID string or a URL pointing to the object
74
- expected_parrot_url: Optional URL for the Parrot server
75
73
  """
76
74
  from edsl.coop import Coop
77
75
  from edsl.coop.utils import ObjectRegistry
@@ -79,36 +77,27 @@ class PersistenceMixin:
79
77
  object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
80
78
  coop = Coop()
81
79
 
82
- # Determine if input is URL or UUID
83
- if "www" in url_or_uuid:
84
- url_or_uuid = url_or_uuid.replace("www", "api")
85
- if url_or_uuid and (
86
- "http://" in str(url_or_uuid) or "https://" in str(url_or_uuid)
87
- ):
88
- return coop.get(url=url_or_uuid, expected_object_type=object_type)
89
- else:
90
- return coop.get(uuid=url_or_uuid, expected_object_type=object_type)
80
+ return coop.get(url_or_uuid, expected_object_type=object_type)
91
81
 
92
82
  @classmethod
93
- def delete(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):
83
+ def delete(cls, url_or_uuid: Union[str, UUID]) -> None:
94
84
  """Delete the object from coop."""
95
85
  from edsl.coop import Coop
96
86
 
97
87
  coop = Coop()
98
- return coop.delete(uuid, url)
88
+
89
+ return coop.delete(url_or_uuid)
99
90
 
100
91
  @classmethod
101
- def patch(
92
+ def patch_cls(
102
93
  cls,
103
- uuid: Optional[Union[str, UUID]] = None,
104
- url: Optional[str] = None,
94
+ url_or_uuid: Union[str, UUID],
105
95
  description: Optional[str] = None,
106
- alias: Optional[str] = None,
107
96
  value: Optional[Any] = None,
108
97
  visibility: Optional[str] = None,
109
98
  ):
110
99
  """
111
- Patch an uploaded objects attributes.
100
+ Patch an uploaded object's attributes (class method version).
112
101
  - `description` changes the description of the object on Coop
113
102
  - `value` changes the value of the object on Coop. **has to be an EDSL object**
114
103
  - `visibility` changes the visibility of the object on Coop
@@ -116,7 +105,85 @@ class PersistenceMixin:
116
105
  from edsl.coop import Coop
117
106
 
118
107
  coop = Coop()
119
- return coop.patch(uuid, url, description, alias, value, visibility)
108
+
109
+ return coop.patch(
110
+ url_or_uuid=url_or_uuid,
111
+ description=description,
112
+ value=value,
113
+ visibility=visibility,
114
+ )
115
+
116
+ class ClassOrInstanceMethod:
117
+ """Descriptor that allows a method to be called as both a class method and an instance method."""
118
+
119
+ def __init__(self, func):
120
+ self.func = func
121
+
122
+ def __get__(self, obj, objtype=None):
123
+ if obj is None:
124
+ # Called as a class method
125
+ def wrapper(*args, **kwargs):
126
+ return self.func(objtype, *args, **kwargs)
127
+
128
+ return wrapper
129
+ else:
130
+ # Called as an instance method
131
+ def wrapper(*args, **kwargs):
132
+ return self.func(obj, *args, **kwargs)
133
+
134
+ return wrapper
135
+
136
+ @ClassOrInstanceMethod
137
+ def patch(
138
+ self_or_cls,
139
+ url_or_uuid: Union[str, UUID],
140
+ description: Optional[str] = None,
141
+ value: Optional[Any] = None,
142
+ visibility: Optional[str] = None,
143
+ ):
144
+ """
145
+ Patch an uploaded object's attributes.
146
+
147
+ When called as a class method:
148
+ - Requires explicit `value` parameter
149
+
150
+ When called as an instance method:
151
+ - Uses the instance itself as the `value` parameter
152
+
153
+ Parameters:
154
+ - `id_or_url`: ID or URL of the object to patch
155
+ - `description`: changes the description of the object on Coop
156
+ - `value`: changes the value of the object on Coop (required for class method)
157
+ - `visibility`: changes the visibility of the object on Coop
158
+ """
159
+
160
+ # Check if this is being called as a class method
161
+ if isinstance(self_or_cls, type):
162
+ # This is a class method call
163
+ cls = self_or_cls
164
+ return cls.patch_cls(
165
+ url_or_uuid=url_or_uuid,
166
+ description=description,
167
+ value=value,
168
+ visibility=visibility,
169
+ )
170
+ else:
171
+ # This is an instance method call
172
+ instance = self_or_cls
173
+ cls_type = instance.__class__
174
+
175
+ # Use the instance as the value if not explicitly provided
176
+ if value is None:
177
+ value = instance
178
+ else:
179
+ pass
180
+
181
+ return cls_type.patch_cls(
182
+ url_or_uuid=url_or_uuid,
183
+ description=description,
184
+ value=value,
185
+ visibility=visibility,
186
+ )
120
187
 
121
188
  @classmethod
122
189
  def search(cls, query):
edsl/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.46"
1
+ __version__ = "0.1.47"
edsl/coop/coop.py CHANGED
@@ -287,19 +287,25 @@ class Coop(CoopFunctionsMixin):
287
287
  if value is None:
288
288
  return "null"
289
289
 
290
+ @staticmethod
291
+ def _is_url(url_or_uuid: Union[str, UUID]) -> bool:
292
+ return "http://" in str(url_or_uuid) or "https://" in str(url_or_uuid)
293
+
290
294
  def _resolve_uuid_or_alias(
291
- self, uuid: Union[str, UUID] = None, url: str = None
295
+ self, url_or_uuid: Union[str, UUID]
292
296
  ) -> tuple[Optional[str], Optional[str], Optional[str]]:
293
297
  """
294
298
  Resolve the uuid or alias information from a uuid or a url.
295
299
  Returns a tuple of (uuid, owner_username, alias)
296
- - For content/<uuid> URLs: returns (uuid, None, None)
297
- - For content/<username>/<alias> URLs: returns (None, username, alias)
300
+ - For content/uuid URLs: returns (uuid, None, None)
301
+ - For content/username/alias URLs: returns (None, username, alias)
298
302
  """
299
- if not url and not uuid:
303
+ if not url_or_uuid:
300
304
  raise CoopNoUUIDError("No uuid or url provided for the object.")
301
305
 
302
- if not uuid and url:
306
+ if self._is_url(url_or_uuid):
307
+ url = str(url_or_uuid)
308
+
303
309
  parts = (
304
310
  url.replace("http://", "")
305
311
  .replace("https://", "")
@@ -326,7 +332,8 @@ class Coop(CoopFunctionsMixin):
326
332
  f"Invalid URL format. The URL must end with /content/<uuid> or /content/<username>/<alias>: {url}"
327
333
  )
328
334
 
329
- return str(uuid), None, None
335
+ uuid = str(url_or_uuid)
336
+ return uuid, None, None
330
337
 
331
338
  @property
332
339
  def edsl_settings(self) -> dict:
@@ -348,6 +355,15 @@ class Coop(CoopFunctionsMixin):
348
355
  ################
349
356
  # Objects
350
357
  ################
358
+ def _get_alias_url(self, owner_username: str, alias: str) -> Union[str, None]:
359
+ """
360
+ Get the URL of an object by its owner username and alias.
361
+ """
362
+ if owner_username and alias:
363
+ return f"{self.url}/content/{owner_username}/{alias}"
364
+ else:
365
+ return None
366
+
351
367
  def create(
352
368
  self,
353
369
  object: EDSLObject,
@@ -368,7 +384,9 @@ class Coop(CoopFunctionsMixin):
368
384
  "json_string": json.dumps(
369
385
  object.to_dict(),
370
386
  default=self._json_handle_none,
371
- ),
387
+ )
388
+ if object_type != "scenario"
389
+ else "",
372
390
  "object_type": object_type,
373
391
  "visibility": visibility,
374
392
  "version": self._edsl_version,
@@ -376,19 +394,38 @@ class Coop(CoopFunctionsMixin):
376
394
  )
377
395
  self._resolve_server_response(response)
378
396
  response_json = response.json()
397
+
398
+ if object_type == "scenario":
399
+ json_data = json.dumps(
400
+ object.to_dict(),
401
+ default=self._json_handle_none,
402
+ )
403
+ headers = {"Content-Type": "application/json"}
404
+ if response_json.get("upload_signed_url"):
405
+ signed_url = response_json.get("upload_signed_url")
406
+ else:
407
+ raise Exception("No signed url provided received")
408
+
409
+ response = requests.put(
410
+ signed_url, data=json_data.encode(), headers=headers
411
+ )
412
+ owner_username = response_json.get("owner_username")
413
+ object_alias = response_json.get("alias")
414
+
379
415
  return {
380
416
  "description": response_json.get("description"),
381
417
  "object_type": object_type,
382
418
  "url": f"{self.url}/content/{response_json.get('uuid')}",
419
+ "alias_url": self._get_alias_url(owner_username, object_alias),
383
420
  "uuid": response_json.get("uuid"),
384
421
  "version": self._edsl_version,
385
422
  "visibility": response_json.get("visibility"),
423
+ "upload_signed_url": response_json.get("upload_signed_url", None),
386
424
  }
387
425
 
388
426
  def get(
389
427
  self,
390
- uuid: Union[str, UUID] = None,
391
- url: str = None,
428
+ url_or_uuid: Union[str, UUID],
392
429
  expected_object_type: Optional[ObjectType] = None,
393
430
  ) -> EDSLObject:
394
431
  """
@@ -396,13 +433,13 @@ class Coop(CoopFunctionsMixin):
396
433
  - If the object's visibility is private, the user must be the owner.
397
434
  - Optionally, check if the retrieved object is of a certain type.
398
435
 
399
- :param uuid: the uuid of the object either in str or UUID format.
400
- :param url: the url of the object (can be content/uuid or content/username/alias format).
401
- :param expected_object_type: the expected type of the object.
436
+ :param url_or_uuid: The UUID or URL of the object.
437
+ URLs can be in the form content/uuid or content/username/alias.
438
+ :param expected_object_type: The expected type of the object.
402
439
 
403
440
  :return: the object instance.
404
441
  """
405
- obj_uuid, owner_username, alias = self._resolve_uuid_or_alias(uuid, url)
442
+ obj_uuid, owner_username, alias = self._resolve_uuid_or_alias(url_or_uuid)
406
443
 
407
444
  if obj_uuid:
408
445
  response = self._send_server_request(
@@ -419,6 +456,10 @@ class Coop(CoopFunctionsMixin):
419
456
 
420
457
  self._resolve_server_response(response)
421
458
  json_string = response.json().get("json_string")
459
+ if "load_from:" in json_string[0:12]:
460
+ load_link = json_string.split("load_from:")[1]
461
+ object_data = requests.get(load_link)
462
+ json_string = object_data.text
422
463
  object_type = response.json().get("object_type")
423
464
  if expected_object_type and object_type != expected_object_type:
424
465
  raise Exception(f"Expected {expected_object_type=} but got {object_type=}")
@@ -437,28 +478,51 @@ class Coop(CoopFunctionsMixin):
437
478
  params={"type": object_type},
438
479
  )
439
480
  self._resolve_server_response(response)
440
- objects = [
441
- {
442
- "object": edsl_class.from_dict(json.loads(o.get("json_string"))),
481
+ objects = []
482
+ for o in response.json():
483
+ json_string = o.get("json_string")
484
+ ## check if load from bucket needed.
485
+ if "load_from:" in json_string[0:12]:
486
+ load_link = json_string.split("load_from:")[1]
487
+ object_data = requests.get(load_link)
488
+ json_string = object_data.text
489
+
490
+ json_string = json.loads(json_string)
491
+ object = {
492
+ "object": edsl_class.from_dict(json_string),
443
493
  "uuid": o.get("uuid"),
444
494
  "version": o.get("version"),
445
495
  "description": o.get("description"),
446
496
  "visibility": o.get("visibility"),
447
497
  "url": f"{self.url}/content/{o.get('uuid')}",
498
+ "alias_url": self._get_alias_url(
499
+ o.get("owner_username"), o.get("alias")
500
+ ),
448
501
  }
449
- for o in response.json()
450
- ]
502
+ objects.append(object)
503
+
451
504
  return objects
452
505
 
453
- def delete(self, uuid: Union[str, UUID] = None, url: str = None) -> dict:
506
+ def delete(self, url_or_uuid: Union[str, UUID]) -> dict:
454
507
  """
455
508
  Delete an object from the server.
509
+
510
+ :param url_or_uuid: The UUID or URL of the object.
511
+ URLs can be in the form content/uuid or content/username/alias.
456
512
  """
457
- obj_uuid, _, _ = self._resolve_uuid_or_alias(uuid, url)
513
+ obj_uuid, owner_username, alias = self._resolve_uuid_or_alias(url_or_uuid)
514
+
515
+ if obj_uuid:
516
+ uri = "api/v0/object"
517
+ params = {"uuid": obj_uuid}
518
+ else:
519
+ uri = "api/v0/object/alias"
520
+ params = {"owner_username": owner_username, "alias": alias}
521
+
458
522
  response = self._send_server_request(
459
- uri=f"api/v0/object",
523
+ uri=uri,
460
524
  method="DELETE",
461
- params={"uuid": obj_uuid},
525
+ params=params,
462
526
  )
463
527
 
464
528
  self._resolve_server_response(response)
@@ -466,8 +530,7 @@ class Coop(CoopFunctionsMixin):
466
530
 
467
531
  def patch(
468
532
  self,
469
- uuid: Union[str, UUID] = None,
470
- url: str = None,
533
+ url_or_uuid: Union[str, UUID],
471
534
  description: Optional[str] = None,
472
535
  alias: Optional[str] = None,
473
536
  value: Optional[EDSLObject] = None,
@@ -475,15 +538,35 @@ class Coop(CoopFunctionsMixin):
475
538
  ) -> dict:
476
539
  """
477
540
  Change the attributes of an uploaded object
478
- - Only supports visibility for now
479
- """
480
- if description is None and visibility is None and value is None:
541
+
542
+ :param url_or_uuid: The UUID or URL of the object.
543
+ URLs can be in the form content/uuid or content/username/alias.
544
+ :param description: Optional new description
545
+ :param alias: Optional new alias
546
+ :param value: Optional new object value
547
+ :param visibility: Optional new visibility setting
548
+ """
549
+ if (
550
+ description is None
551
+ and visibility is None
552
+ and value is None
553
+ and alias is None
554
+ ):
481
555
  raise Exception("Nothing to patch.")
482
- obj_uuid, _, _ = self._resolve_uuid_or_alias(uuid, url)
556
+
557
+ obj_uuid, owner_username, obj_alias = self._resolve_uuid_or_alias(url_or_uuid)
558
+
559
+ if obj_uuid:
560
+ uri = "api/v0/object"
561
+ params = {"uuid": obj_uuid}
562
+ else:
563
+ uri = "api/v0/object/alias"
564
+ params = {"owner_username": owner_username, "alias": obj_alias}
565
+
483
566
  response = self._send_server_request(
484
- uri=f"api/v0/object",
567
+ uri=uri,
485
568
  method="PATCH",
486
- params={"uuid": obj_uuid},
569
+ params=params,
487
570
  payload={
488
571
  "description": description,
489
572
  "alias": alias,
@@ -1187,27 +1270,25 @@ def main():
1187
1270
  ##############
1188
1271
  # .. create and manipulate an object through the Coop client
1189
1272
  response = coop.create(QuestionMultipleChoice.example())
1190
- coop.get(uuid=response.get("uuid"))
1191
- coop.get(uuid=response.get("uuid"), expected_object_type="question")
1192
- coop.get(url=response.get("url"))
1273
+ coop.get(response.get("uuid"))
1274
+ coop.get(response.get("uuid"), expected_object_type="question")
1275
+ coop.get(response.get("url"))
1193
1276
  coop.create(QuestionMultipleChoice.example())
1194
1277
  coop.get_all("question")
1195
- coop.patch(uuid=response.get("uuid"), visibility="private")
1196
- coop.patch(uuid=response.get("uuid"), description="hey")
1197
- coop.patch(uuid=response.get("uuid"), value=QuestionFreeText.example())
1198
- # coop.patch(uuid=response.get("uuid"), value=Survey.example()) - should throw error
1199
- coop.get(uuid=response.get("uuid"))
1200
- coop.delete(uuid=response.get("uuid"))
1278
+ coop.patch(response.get("uuid"), visibility="private")
1279
+ coop.patch(response.get("uuid"), description="hey")
1280
+ coop.patch(response.get("uuid"), value=QuestionFreeText.example())
1281
+ # coop.patch(response.get("uuid"), value=Survey.example()) - should throw error
1282
+ coop.get(response.get("uuid"))
1283
+ coop.delete(response.get("uuid"))
1201
1284
 
1202
1285
  # .. create and manipulate an object through the class
1203
1286
  response = QuestionMultipleChoice.example().push()
1204
- QuestionMultipleChoice.pull(uuid=response.get("uuid"))
1205
- QuestionMultipleChoice.pull(url=response.get("url"))
1206
- QuestionMultipleChoice.patch(uuid=response.get("uuid"), visibility="private")
1207
- QuestionMultipleChoice.patch(uuid=response.get("uuid"), description="hey")
1208
- QuestionMultipleChoice.patch(
1209
- uuid=response.get("uuid"), value=QuestionFreeText.example()
1210
- )
1287
+ QuestionMultipleChoice.pull(response.get("uuid"))
1288
+ QuestionMultipleChoice.pull(response.get("url"))
1289
+ QuestionMultipleChoice.patch(response.get("uuid"), visibility="private")
1290
+ QuestionMultipleChoice.patch(response.get("uuid"), description="hey")
1291
+ QuestionMultipleChoice.patch(response.get("uuid"), value=QuestionFreeText.example())
1211
1292
  QuestionMultipleChoice.pull(response.get("uuid"))
1212
1293
  QuestionMultipleChoice.delete(response.get("uuid"))
1213
1294
 
@@ -1230,7 +1311,7 @@ def main():
1230
1311
  # 1. Delete existing objects
1231
1312
  existing_objects = coop.get_all(object_type)
1232
1313
  for item in existing_objects:
1233
- coop.delete(uuid=item.get("uuid"))
1314
+ coop.delete(item.get("uuid"))
1234
1315
  # 2. Create new objects
1235
1316
  example = cls.example()
1236
1317
  response_1 = coop.create(example)
@@ -1244,21 +1325,21 @@ def main():
1244
1325
  assert len(objects) == 4
1245
1326
  # 4. Try to retrieve an item that does not exist
1246
1327
  try:
1247
- coop.get(uuid=uuid4())
1328
+ coop.get(uuid4())
1248
1329
  except Exception as e:
1249
1330
  print(e)
1250
1331
  # 5. Try to retrieve all test objects by their uuids
1251
1332
  for response in [response_1, response_2, response_3, response_4]:
1252
- coop.get(uuid=response.get("uuid"))
1333
+ coop.get(response.get("uuid"))
1253
1334
  # 6. Change visibility of all objects
1254
1335
  for item in objects:
1255
- coop.patch(uuid=item.get("uuid"), visibility="private")
1336
+ coop.patch(item.get("uuid"), visibility="private")
1256
1337
  # 6. Change description of all objects
1257
1338
  for item in objects:
1258
- coop.patch(uuid=item.get("uuid"), description="hey")
1339
+ coop.patch(item.get("uuid"), description="hey")
1259
1340
  # 7. Delete all objects
1260
1341
  for item in objects:
1261
- coop.delete(uuid=item.get("uuid"))
1342
+ coop.delete(item.get("uuid"))
1262
1343
  assert len(coop.get_all(object_type)) == 0
1263
1344
 
1264
1345
  ##############
@@ -1291,4 +1372,4 @@ def main():
1291
1372
  coop.remote_inference_cost(job)
1292
1373
  job_coop_object = coop.remote_inference_create(job)
1293
1374
  job_coop_results = coop.remote_inference_get(job_coop_object.get("uuid"))
1294
- coop.get(uuid=job_coop_results.get("results_uuid"))
1375
+ coop.get(job_coop_results.get("results_uuid"))
edsl/data/Cache.py CHANGED
@@ -173,6 +173,7 @@ class Cache(Base):
173
173
  user_prompt: str,
174
174
  response: dict,
175
175
  iteration: int,
176
+ service: str,
176
177
  ) -> str:
177
178
  """
178
179
  Add a new key-value pair to the cache.
@@ -204,6 +205,7 @@ class Cache(Base):
204
205
  user_prompt=user_prompt,
205
206
  output=json.dumps(response),
206
207
  iteration=iteration,
208
+ service=service,
207
209
  )
208
210
  key = entry.key
209
211
  self.new_entries[key] = entry
edsl/data/CacheEntry.py CHANGED
@@ -16,7 +16,7 @@ class CacheEntry(RepresentationMixin):
16
16
  """
17
17
 
18
18
  key_fields = ["model", "parameters", "system_prompt", "user_prompt", "iteration"]
19
- all_fields = key_fields + ["timestamp", "output"]
19
+ all_fields = key_fields + ["timestamp", "output", "service"]
20
20
 
21
21
  def __init__(
22
22
  self,
@@ -28,6 +28,7 @@ class CacheEntry(RepresentationMixin):
28
28
  iteration: Optional[int] = None,
29
29
  output: str,
30
30
  timestamp: Optional[int] = None,
31
+ service: Optional[str] = None,
31
32
  ):
32
33
  self.model = model
33
34
  self.parameters = parameters
@@ -38,6 +39,7 @@ class CacheEntry(RepresentationMixin):
38
39
  self.timestamp = timestamp or int(
39
40
  datetime.datetime.now(datetime.timezone.utc).timestamp()
40
41
  )
42
+ self.service = service
41
43
  self._check_types()
42
44
 
43
45
  def _check_types(self):
@@ -59,6 +61,8 @@ class CacheEntry(RepresentationMixin):
59
61
  # TODO: should probably be float
60
62
  if not isinstance(self.timestamp, int):
61
63
  raise TypeError(f"`timestamp` should be an integer")
64
+ if self.service is not None and not isinstance(self.service, str):
65
+ raise TypeError("`service` should be either a string or None")
62
66
 
63
67
  @classmethod
64
68
  def gen_key(
@@ -94,6 +98,7 @@ class CacheEntry(RepresentationMixin):
94
98
  "output": self.output,
95
99
  "iteration": self.iteration,
96
100
  "timestamp": self.timestamp,
101
+ "service": self.service,
97
102
  }
98
103
  # if add_edsl_version:
99
104
  # from edsl import __version__
@@ -144,7 +149,8 @@ class CacheEntry(RepresentationMixin):
144
149
  f"user_prompt={repr(self.user_prompt)}, "
145
150
  f"output={repr(self.output)}, "
146
151
  f"iteration={self.iteration}, "
147
- f"timestamp={self.timestamp})"
152
+ f"timestamp={self.timestamp}, "
153
+ f"service={repr(self.service)})"
148
154
  )
149
155
 
150
156
  @classmethod
@@ -164,6 +170,7 @@ class CacheEntry(RepresentationMixin):
164
170
  output="The fox says 'hello'",
165
171
  iteration=1,
166
172
  timestamp=int(datetime.datetime.now(datetime.timezone.utc).timestamp()),
173
+ service="openai",
167
174
  )
168
175
 
169
176
  @classmethod
@@ -184,6 +191,7 @@ class CacheEntry(RepresentationMixin):
184
191
  input = cls.example().to_dict()
185
192
  _ = input.pop("timestamp")
186
193
  _ = input.pop("output")
194
+ _ = input.pop("service")
187
195
  return input
188
196
 
189
197
  @classmethod
@@ -29,9 +29,12 @@ class PerplexityService(OpenAIService):
29
29
  @classmethod
30
30
  def available(cls) -> List[str]:
31
31
  return [
32
- "llama-3.1-sonar-huge-128k-online",
33
- "llama-3.1-sonar-large-128k-online",
34
- "llama-3.1-sonar-small-128k-online",
32
+ "sonar-deep-research",
33
+ "sonar-reasoning-pro",
34
+ "sonar-reasoning",
35
+ "sonar-pro",
36
+ "sonar",
37
+ "r1-1776",
35
38
  ]
36
39
 
37
40
  @classmethod
@@ -65,10 +68,10 @@ class PerplexityService(OpenAIService):
65
68
  }
66
69
 
67
70
  def sync_client(self):
68
- return cls.sync_client()
71
+ return cls.sync_client(api_key=self.api_token)
69
72
 
70
73
  def async_client(self):
71
- return cls.async_client()
74
+ return cls.async_client(api_key=self.api_token)
72
75
 
73
76
  @classmethod
74
77
  def available(cls) -> list[str]:
@@ -149,6 +152,7 @@ class PerplexityService(OpenAIService):
149
152
  # "logprobs": self.logprobs,
150
153
  # "top_logprobs": self.top_logprobs if self.logprobs else None,
151
154
  }
155
+ print("calling the model", flush=True)
152
156
  try:
153
157
  response = await client.chat.completions.create(**params)
154
158
  except Exception as e:
edsl/jobs/Jobs.py CHANGED
@@ -119,6 +119,19 @@ class Jobs(Base):
119
119
  :param agents: a list of agents
120
120
  :param models: a list of models
121
121
  :param scenarios: a list of scenarios
122
+
123
+
124
+ >>> from edsl.surveys.Survey import Survey
125
+ >>> from edsl.questions.QuestionFreeText import QuestionFreeText
126
+ >>> q = QuestionFreeText(question_name="name", question_text="What is your name?")
127
+ >>> s = Survey(questions=[q])
128
+ >>> j = Jobs(survey = s)
129
+ >>> q = QuestionFreeText(question_name="{{ bad_name }}", question_text="What is your name?")
130
+ >>> s = Survey(questions=[q])
131
+ >>> j = Jobs(survey = s)
132
+ Traceback (most recent call last):
133
+ ...
134
+ ValueError: At least some question names are not valid: ['{{ bad_name }}']
122
135
  """
123
136
  self.run_config = RunConfig(
124
137
  environment=RunEnvironment(), parameters=RunParameters()
@@ -129,6 +142,13 @@ class Jobs(Base):
129
142
  self.scenarios: ScenarioList = scenarios
130
143
  self.models: ModelList = models
131
144
 
145
+ try:
146
+ assert self.survey.question_names_valid()
147
+ except Exception as e:
148
+ invalid_question_names = [q.question_name for q in self.survey.questions if not q.is_valid_question_name()]
149
+ raise ValueError(f"At least some question names are not valid: {invalid_question_names}")
150
+
151
+
132
152
  def add_running_env(self, running_env: RunEnvironment):
133
153
  self.run_config.add_environment(running_env)
134
154
  return self
@@ -153,7 +153,8 @@ class JobsComponentConstructor:
153
153
  For example, if the user passes in 3 agents,
154
154
  and there are 2 existing agents, this will create 6 new agents
155
155
  >>> from edsl.jobs import Jobs
156
- >>> JobsComponentConstructor(Jobs(survey = []))._merge_objects([1,2,3], [4,5,6])
156
+ >>> from edsl.surveys.Survey import Survey
157
+ >>> JobsComponentConstructor(Jobs(survey = Survey.example()))._merge_objects([1,2,3], [4,5,6])
157
158
  [5, 6, 7, 6, 7, 8, 7, 8, 9]
158
159
  """
159
160
  new_objects = JobsComponentConstructor._get_empty_container_object(