edsl 0.1.61__py3-none-any.whl → 0.1.62__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/base_class.py +8 -0
- edsl/coop/coop.py +263 -16
- edsl/coop/coop_jobs_objects.py +2 -2
- edsl/coop/coop_regular_objects.py +3 -1
- edsl/jobs/jobs.py +1 -1
- edsl/jobs/remote_inference.py +4 -4
- edsl/prompts/prompt.py +7 -2
- edsl/questions/question_registry.py +4 -1
- edsl/scenarios/file_store.py +69 -0
- edsl/scenarios/scenario.py +233 -0
- edsl/scenarios/scenario_source.py +0 -1
- {edsl-0.1.61.dist-info → edsl-0.1.62.dist-info}/METADATA +1 -1
- {edsl-0.1.61.dist-info → edsl-0.1.62.dist-info}/RECORD +17 -17
- {edsl-0.1.61.dist-info → edsl-0.1.62.dist-info}/LICENSE +0 -0
- {edsl-0.1.61.dist-info → edsl-0.1.62.dist-info}/WHEEL +0 -0
- {edsl-0.1.61.dist-info → edsl-0.1.62.dist-info}/entry_points.txt +0 -0
edsl/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.62"
|
edsl/base/base_class.py
CHANGED
@@ -331,9 +331,17 @@ class PersistenceMixin:
|
|
331
331
|
"""
|
332
332
|
from edsl.coop import Coop
|
333
333
|
from edsl.coop import ObjectRegistry
|
334
|
+
from edsl.jobs import Jobs
|
334
335
|
|
335
336
|
coop = Coop(url=expected_parrot_url)
|
336
337
|
|
338
|
+
if issubclass(cls, Jobs):
|
339
|
+
job_data = coop.new_remote_inference_get(
|
340
|
+
str(url_or_uuid), include_json_string=True
|
341
|
+
)
|
342
|
+
job_dict = json.loads(job_data.get("job_json_string"))
|
343
|
+
return cls.from_dict(job_dict)
|
344
|
+
|
337
345
|
object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
|
338
346
|
|
339
347
|
return coop.pull(url_or_uuid, object_type)
|
edsl/coop/coop.py
CHANGED
@@ -598,7 +598,7 @@ class Coop(CoopFunctionsMixin):
|
|
598
598
|
else:
|
599
599
|
from .exceptions import CoopResponseError
|
600
600
|
|
601
|
-
raise CoopResponseError("No signed url was provided
|
601
|
+
raise CoopResponseError("No signed url was provided.")
|
602
602
|
|
603
603
|
response = requests.put(
|
604
604
|
signed_url, data=json_data.encode(), headers=headers
|
@@ -945,18 +945,31 @@ class Coop(CoopFunctionsMixin):
|
|
945
945
|
|
946
946
|
obj_uuid, owner_username, obj_alias = self._resolve_uuid_or_alias(url_or_uuid)
|
947
947
|
|
948
|
-
# If we
|
949
|
-
if
|
950
|
-
#
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
948
|
+
# If we're updating the value, we need to check the storage format
|
949
|
+
if value:
|
950
|
+
# If we don't have a UUID but have an alias, get the UUID and format info first
|
951
|
+
if not obj_uuid and owner_username and obj_alias:
|
952
|
+
# Get object info including UUID and format
|
953
|
+
info_response = self._send_server_request(
|
954
|
+
uri="api/v0/object/alias/info",
|
955
|
+
method="GET",
|
956
|
+
params={"owner_username": owner_username, "alias": obj_alias},
|
957
|
+
)
|
958
|
+
self._resolve_server_response(info_response)
|
959
|
+
info_data = info_response.json()
|
958
960
|
|
959
|
-
|
961
|
+
obj_uuid = info_data.get("uuid")
|
962
|
+
is_new_format = info_data.get("is_new_format", False)
|
963
|
+
else:
|
964
|
+
# We have a UUID, check the format
|
965
|
+
format_check_response = self._send_server_request(
|
966
|
+
uri="api/v0/object/check-format",
|
967
|
+
method="POST",
|
968
|
+
payload={"object_uuid": str(obj_uuid)},
|
969
|
+
)
|
970
|
+
self._resolve_server_response(format_check_response)
|
971
|
+
format_data = format_check_response.json()
|
972
|
+
is_new_format = format_data.get("is_new_format", False)
|
960
973
|
|
961
974
|
if is_new_format:
|
962
975
|
# Handle new format objects: update metadata first, then upload content
|
@@ -1052,10 +1065,20 @@ class Coop(CoopFunctionsMixin):
|
|
1052
1065
|
f"Failed to upload object to GCS: {gcs_response.status_code}"
|
1053
1066
|
)
|
1054
1067
|
|
1068
|
+
# Step 4: Confirm upload and trigger queue worker processing
|
1069
|
+
confirm_response = self._send_server_request(
|
1070
|
+
uri="api/v0/object/confirm-upload",
|
1071
|
+
method="POST",
|
1072
|
+
payload={"object_uuid": str(obj_uuid)},
|
1073
|
+
)
|
1074
|
+
self._resolve_server_response(confirm_response)
|
1075
|
+
confirm_data = confirm_response.json()
|
1076
|
+
|
1055
1077
|
return {
|
1056
1078
|
"status": "success",
|
1057
|
-
"message": "Object updated successfully (new format - uploaded to GCS)",
|
1079
|
+
"message": "Object updated successfully (new format - uploaded to GCS and processing triggered)",
|
1058
1080
|
"object_uuid": str(obj_uuid),
|
1081
|
+
"processing_started": confirm_data.get("processing_started", False),
|
1059
1082
|
}
|
1060
1083
|
|
1061
1084
|
################
|
@@ -1195,7 +1218,7 @@ class Coop(CoopFunctionsMixin):
|
|
1195
1218
|
if not upload_signed_url:
|
1196
1219
|
from .exceptions import CoopResponseError
|
1197
1220
|
|
1198
|
-
raise CoopResponseError("No signed url was provided
|
1221
|
+
raise CoopResponseError("No signed url was provided.")
|
1199
1222
|
|
1200
1223
|
response = requests.put(
|
1201
1224
|
upload_signed_url,
|
@@ -1431,6 +1454,160 @@ class Coop(CoopFunctionsMixin):
|
|
1431
1454
|
}
|
1432
1455
|
)
|
1433
1456
|
|
1457
|
+
def new_remote_inference_get(
|
1458
|
+
self,
|
1459
|
+
job_uuid: Optional[str] = None,
|
1460
|
+
results_uuid: Optional[str] = None,
|
1461
|
+
include_json_string: Optional[bool] = False,
|
1462
|
+
) -> RemoteInferenceResponse:
|
1463
|
+
"""
|
1464
|
+
Get the status and details of a remote inference job.
|
1465
|
+
|
1466
|
+
This method retrieves the current status and information about a remote job,
|
1467
|
+
including links to results if the job has completed successfully.
|
1468
|
+
|
1469
|
+
Parameters:
|
1470
|
+
job_uuid (str, optional): The UUID of the remote job to check
|
1471
|
+
results_uuid (str, optional): The UUID of the results associated with the job
|
1472
|
+
(can be used if you only have the results UUID)
|
1473
|
+
include_json_string (bool, optional): If True, include the json string for the job in the response
|
1474
|
+
|
1475
|
+
Returns:
|
1476
|
+
RemoteInferenceResponse: Information about the job including:
|
1477
|
+
job_uuid: The unique identifier for the job
|
1478
|
+
results_uuid: The UUID of the results
|
1479
|
+
results_url: URL to access the results
|
1480
|
+
status: Current status ("queued", "running", "completed", "failed")
|
1481
|
+
version: EDSL version used for the job
|
1482
|
+
job_json_string: The json string for the job (if include_json_string is True)
|
1483
|
+
latest_job_run_details: Metadata about the job status
|
1484
|
+
interview_details: Metadata about the job interview status (for jobs that have reached running status)
|
1485
|
+
total_interviews: The total number of interviews in the job
|
1486
|
+
completed_interviews: The number of completed interviews
|
1487
|
+
interviews_with_exceptions: The number of completed interviews that have exceptions
|
1488
|
+
exception_counters: A list of exception counts for the job
|
1489
|
+
exception_type: The type of exception
|
1490
|
+
inference_service: The inference service
|
1491
|
+
model: The model
|
1492
|
+
question_name: The name of the question
|
1493
|
+
exception_count: The number of exceptions
|
1494
|
+
failure_reason: The reason the job failed (failed jobs only)
|
1495
|
+
failure_description: The description of the failure (failed jobs only)
|
1496
|
+
error_report_uuid: The UUID of the error report (partially failed jobs only)
|
1497
|
+
cost_credits: The cost of the job run in credits
|
1498
|
+
cost_usd: The cost of the job run in USD
|
1499
|
+
expenses: The expenses incurred by the job run
|
1500
|
+
service: The service
|
1501
|
+
model: The model
|
1502
|
+
token_type: The type of token (input or output)
|
1503
|
+
price_per_million_tokens: The price per million tokens
|
1504
|
+
tokens_count: The number of tokens consumed
|
1505
|
+
cost_credits: The cost of the service/model/token type combination in credits
|
1506
|
+
cost_usd: The cost of the service/model/token type combination in USD
|
1507
|
+
|
1508
|
+
Raises:
|
1509
|
+
ValueError: If neither job_uuid nor results_uuid is provided
|
1510
|
+
CoopServerResponseError: If there's an error communicating with the server
|
1511
|
+
|
1512
|
+
Notes:
|
1513
|
+
- Either job_uuid or results_uuid must be provided
|
1514
|
+
- If both are provided, job_uuid takes precedence
|
1515
|
+
- For completed jobs, you can use the results_url to view or download results
|
1516
|
+
- For failed jobs, check the latest_error_report_url for debugging information
|
1517
|
+
|
1518
|
+
Example:
|
1519
|
+
>>> job_status = coop.new_remote_inference_get("9f8484ee-b407-40e4-9652-4133a7236c9c")
|
1520
|
+
>>> print(f"Job status: {job_status['status']}")
|
1521
|
+
>>> if job_status['status'] == 'completed':
|
1522
|
+
... print(f"Results available at: {job_status['results_url']}")
|
1523
|
+
"""
|
1524
|
+
if job_uuid is None and results_uuid is None:
|
1525
|
+
from .exceptions import CoopValueError
|
1526
|
+
|
1527
|
+
raise CoopValueError("Either job_uuid or results_uuid must be provided.")
|
1528
|
+
elif job_uuid is not None:
|
1529
|
+
params = {"job_uuid": job_uuid}
|
1530
|
+
else:
|
1531
|
+
params = {"results_uuid": results_uuid}
|
1532
|
+
if include_json_string:
|
1533
|
+
params["include_json_string"] = include_json_string
|
1534
|
+
|
1535
|
+
response = self._send_server_request(
|
1536
|
+
uri="api/v0/remote-inference",
|
1537
|
+
method="GET",
|
1538
|
+
params=params,
|
1539
|
+
)
|
1540
|
+
self._resolve_server_response(response)
|
1541
|
+
data = response.json()
|
1542
|
+
|
1543
|
+
results_uuid = data.get("results_uuid")
|
1544
|
+
|
1545
|
+
if results_uuid is None:
|
1546
|
+
results_url = None
|
1547
|
+
else:
|
1548
|
+
results_url = f"{self.url}/content/{results_uuid}"
|
1549
|
+
|
1550
|
+
latest_job_run_details = data.get("latest_job_run_details", {})
|
1551
|
+
if data.get("status") == "partial_failed":
|
1552
|
+
latest_error_report_uuid = latest_job_run_details.get("error_report_uuid")
|
1553
|
+
if latest_error_report_uuid is None:
|
1554
|
+
latest_job_run_details["error_report_url"] = None
|
1555
|
+
else:
|
1556
|
+
latest_error_report_url = (
|
1557
|
+
f"{self.url}/home/remote-inference/error/{latest_error_report_uuid}"
|
1558
|
+
)
|
1559
|
+
latest_job_run_details["error_report_url"] = latest_error_report_url
|
1560
|
+
|
1561
|
+
json_string = data.get("job_json_string")
|
1562
|
+
|
1563
|
+
# The job has been offloaded to GCS
|
1564
|
+
if include_json_string and json_string == "offloaded":
|
1565
|
+
|
1566
|
+
# Attempt to fetch JSON string from GCS
|
1567
|
+
response = self._send_server_request(
|
1568
|
+
uri="api/v0/remote-inference/pull",
|
1569
|
+
method="POST",
|
1570
|
+
payload={"job_uuid": job_uuid},
|
1571
|
+
)
|
1572
|
+
# Handle any errors in the response
|
1573
|
+
self._resolve_server_response(response)
|
1574
|
+
if "signed_url" not in response.json():
|
1575
|
+
from .exceptions import CoopResponseError
|
1576
|
+
|
1577
|
+
raise CoopResponseError("No signed url was provided.")
|
1578
|
+
signed_url = response.json().get("signed_url")
|
1579
|
+
|
1580
|
+
if signed_url == "": # The job is in legacy format
|
1581
|
+
job_json = json_string
|
1582
|
+
|
1583
|
+
try:
|
1584
|
+
response = requests.get(signed_url)
|
1585
|
+
self._resolve_gcs_response(response)
|
1586
|
+
job_json = json.dumps(response.json())
|
1587
|
+
except Exception:
|
1588
|
+
job_json = json_string
|
1589
|
+
|
1590
|
+
# If the job is in legacy format, we should already have the JSON string
|
1591
|
+
# from the first API call
|
1592
|
+
elif include_json_string and not json_string == "offloaded":
|
1593
|
+
job_json = json_string
|
1594
|
+
|
1595
|
+
# If include_json_string is False, we don't need the JSON string at all
|
1596
|
+
else:
|
1597
|
+
job_json = None
|
1598
|
+
|
1599
|
+
return RemoteInferenceResponse(
|
1600
|
+
**{
|
1601
|
+
"job_uuid": data.get("job_uuid"),
|
1602
|
+
"results_uuid": results_uuid,
|
1603
|
+
"results_url": results_url,
|
1604
|
+
"status": data.get("status"),
|
1605
|
+
"version": data.get("version"),
|
1606
|
+
"job_json_string": job_json,
|
1607
|
+
"latest_job_run_details": latest_job_run_details,
|
1608
|
+
}
|
1609
|
+
)
|
1610
|
+
|
1434
1611
|
def _validate_remote_job_status_types(
|
1435
1612
|
self, status: Union[RemoteJobStatus, List[RemoteJobStatus]]
|
1436
1613
|
) -> List[RemoteJobStatus]:
|
@@ -2470,7 +2647,7 @@ class Coop(CoopFunctionsMixin):
|
|
2470
2647
|
if "signed_url" not in response.json():
|
2471
2648
|
from .exceptions import CoopResponseError
|
2472
2649
|
|
2473
|
-
raise CoopResponseError("No signed url was provided
|
2650
|
+
raise CoopResponseError("No signed url was provided.")
|
2474
2651
|
signed_url = response.json().get("signed_url")
|
2475
2652
|
|
2476
2653
|
if signed_url == "": # it is in old format
|
@@ -2872,6 +3049,53 @@ class Coop(CoopFunctionsMixin):
|
|
2872
3049
|
self._resolve_server_response(response)
|
2873
3050
|
return response.json()
|
2874
3051
|
|
3052
|
+
def pay_for_service(
|
3053
|
+
self,
|
3054
|
+
credits_transferred: int,
|
3055
|
+
recipient_username: str,
|
3056
|
+
service_name: str,
|
3057
|
+
) -> dict:
|
3058
|
+
"""
|
3059
|
+
Pay for a service.
|
3060
|
+
|
3061
|
+
This method transfers a specified number of credits from the authenticated user's
|
3062
|
+
account to another user's account on the Expected Parrot platform.
|
3063
|
+
|
3064
|
+
Parameters:
|
3065
|
+
credits_transferred (int): The number of credits to transfer to the recipient
|
3066
|
+
recipient_username (str): The username of the recipient
|
3067
|
+
service_name (str): The name of the service to pay for
|
3068
|
+
|
3069
|
+
Returns:
|
3070
|
+
dict: Information about the transfer transaction, including:
|
3071
|
+
- success: Whether the transaction was successful
|
3072
|
+
- transaction_id: A unique identifier for the transaction
|
3073
|
+
- remaining_credits: The number of credits remaining in the sender's account
|
3074
|
+
|
3075
|
+
Raises:
|
3076
|
+
CoopServerResponseError: If there's an error communicating with the server
|
3077
|
+
or if the transfer criteria aren't met (e.g., insufficient credits)
|
3078
|
+
|
3079
|
+
Example:
|
3080
|
+
>>> result = coop.pay_for_service(
|
3081
|
+
... credits_transferred=100,
|
3082
|
+
... service_name="service_name",
|
3083
|
+
... recipient_username="friend_username",
|
3084
|
+
... )
|
3085
|
+
>>> print(f"Transfer successful! You have {result['remaining_credits']} credits left.")
|
3086
|
+
"""
|
3087
|
+
response = self._send_server_request(
|
3088
|
+
uri="api/v0/users/pay-for-service",
|
3089
|
+
method="POST",
|
3090
|
+
payload={
|
3091
|
+
"cost_credits": credits_transferred,
|
3092
|
+
"service_name": service_name,
|
3093
|
+
"recipient_username": recipient_username,
|
3094
|
+
},
|
3095
|
+
)
|
3096
|
+
self._resolve_server_response(response)
|
3097
|
+
return response.json()
|
3098
|
+
|
2875
3099
|
def get_balance(self) -> dict:
|
2876
3100
|
"""
|
2877
3101
|
Get the current credit balance for the authenticated user.
|
@@ -2897,6 +3121,29 @@ class Coop(CoopFunctionsMixin):
|
|
2897
3121
|
self._resolve_server_response(response)
|
2898
3122
|
return response.json()
|
2899
3123
|
|
3124
|
+
def get_profile(self) -> dict:
|
3125
|
+
"""
|
3126
|
+
Get the current user's profile information.
|
3127
|
+
|
3128
|
+
This method retrieves the authenticated user's profile information from
|
3129
|
+
the Expected Parrot platform using their API key.
|
3130
|
+
|
3131
|
+
Returns:
|
3132
|
+
dict: User profile information including:
|
3133
|
+
- username: The user's username
|
3134
|
+
- email: The user's email address
|
3135
|
+
|
3136
|
+
Raises:
|
3137
|
+
CoopServerResponseError: If there's an error communicating with the server
|
3138
|
+
|
3139
|
+
Example:
|
3140
|
+
>>> profile = coop.get_profile()
|
3141
|
+
>>> print(f"Welcome, {profile['username']}!")
|
3142
|
+
"""
|
3143
|
+
response = self._send_server_request(uri="api/v0/users/profile", method="GET")
|
3144
|
+
self._resolve_server_response(response)
|
3145
|
+
return response.json()
|
3146
|
+
|
2900
3147
|
def login_gradio(self, timeout: int = 120, launch: bool = True, **launch_kwargs):
|
2901
3148
|
"""
|
2902
3149
|
Start the EDSL auth token login flow inside a **Gradio** application.
|
@@ -3174,7 +3421,7 @@ def main():
|
|
3174
3421
|
job = Jobs.example()
|
3175
3422
|
coop.remote_inference_cost(job)
|
3176
3423
|
job_coop_object = coop.remote_inference_create(job)
|
3177
|
-
job_coop_results = coop.
|
3424
|
+
job_coop_results = coop.new_remote_inference_get(job_coop_object.get("uuid"))
|
3178
3425
|
coop.get(job_coop_results.get("results_uuid"))
|
3179
3426
|
|
3180
3427
|
import streamlit as st
|
edsl/coop/coop_jobs_objects.py
CHANGED
@@ -26,7 +26,7 @@ class CoopJobsObjects(CoopObjects):
|
|
26
26
|
|
27
27
|
c = Coop()
|
28
28
|
job_details = [
|
29
|
-
c.
|
29
|
+
c.new_remote_inference_get(obj["uuid"], include_json_string=True)
|
30
30
|
for obj in self
|
31
31
|
]
|
32
32
|
|
@@ -53,7 +53,7 @@ class CoopJobsObjects(CoopObjects):
|
|
53
53
|
|
54
54
|
for obj in self:
|
55
55
|
if obj.get("results_uuid"):
|
56
|
-
result = c.
|
56
|
+
result = c.pull(obj["results_uuid"], expected_object_type="results")
|
57
57
|
results.append(result)
|
58
58
|
|
59
59
|
return results
|
edsl/jobs/jobs.py
CHANGED
@@ -1120,7 +1120,7 @@ class Jobs(Base):
|
|
1120
1120
|
raise CoopValueError(
|
1121
1121
|
"You must specify both a scenario list and a scenario list method to use scenarios with your survey."
|
1122
1122
|
)
|
1123
|
-
elif scenario_list_method
|
1123
|
+
elif scenario_list_method == "loop":
|
1124
1124
|
questions, long_scenario_list = self.survey.to_long_format(self.scenarios)
|
1125
1125
|
|
1126
1126
|
# Replace the questions with new ones from the loop method
|
edsl/jobs/remote_inference.py
CHANGED
@@ -176,7 +176,7 @@ class JobsRemoteInferenceHandler:
|
|
176
176
|
from ..coop import Coop
|
177
177
|
|
178
178
|
coop = Coop()
|
179
|
-
return coop.
|
179
|
+
return coop.new_remote_inference_get(job_uuid)
|
180
180
|
|
181
181
|
def _construct_remote_job_fetcher(
|
182
182
|
self, testing_simulated_response: Optional[Any] = None
|
@@ -445,9 +445,9 @@ class JobsRemoteInferenceHandler:
|
|
445
445
|
model_cost_dict["input_cost_credits_with_cache"] = converter.usd_to_credits(
|
446
446
|
input_cost_with_cache
|
447
447
|
)
|
448
|
-
model_cost_dict[
|
449
|
-
|
450
|
-
|
448
|
+
model_cost_dict["output_cost_credits_with_cache"] = (
|
449
|
+
converter.usd_to_credits(output_cost_with_cache)
|
450
|
+
)
|
451
451
|
return list(expenses_by_model.values())
|
452
452
|
|
453
453
|
def _fetch_results_and_log(
|
edsl/prompts/prompt.py
CHANGED
@@ -305,8 +305,13 @@ class Prompt(PersistenceMixin, RepresentationMixin):
|
|
305
305
|
Returns (rendered_text, captured_variables).
|
306
306
|
"""
|
307
307
|
# Combine replacements.
|
308
|
-
|
309
|
-
|
308
|
+
from ..scenarios import Scenario
|
309
|
+
# This fixed Issue 2027 - the scenario prefix was not being recoginized in the template
|
310
|
+
if isinstance(primary_replacement, Scenario):
|
311
|
+
additional = {'scenario': primary_replacement.to_dict()}
|
312
|
+
else:
|
313
|
+
additional = {}
|
314
|
+
all_replacements = {**primary_replacement, **additional_replacements, **additional}
|
310
315
|
# If no replacements and no Jinja variables, just return the text.
|
311
316
|
if not all_replacements and not _find_template_variables(text):
|
312
317
|
return text, template_vars.get_all()
|
@@ -43,6 +43,7 @@ class Question(metaclass=Meta):
|
|
43
43
|
subclass = get_question_classes.get(question_type, None)
|
44
44
|
if subclass is None:
|
45
45
|
from .exceptions import QuestionValueError
|
46
|
+
|
46
47
|
raise QuestionValueError(
|
47
48
|
f"No question registered with question_type {question_type}"
|
48
49
|
)
|
@@ -65,7 +66,7 @@ class Question(metaclass=Meta):
|
|
65
66
|
from ..coop import Coop
|
66
67
|
|
67
68
|
coop = Coop()
|
68
|
-
return coop.
|
69
|
+
return coop.pull(url_or_uuid, "question")
|
69
70
|
|
70
71
|
@classmethod
|
71
72
|
def delete(cls, url_or_uuid: Union[str, UUID]):
|
@@ -146,6 +147,7 @@ def get_question_class(question_type):
|
|
146
147
|
q2c = RegisterQuestionsMeta.question_types_to_classes()
|
147
148
|
if question_type not in q2c:
|
148
149
|
from .exceptions import QuestionValueError
|
150
|
+
|
149
151
|
raise QuestionValueError(
|
150
152
|
f"The question type, {question_type}, is not recognized. Recognied types are: {q2c.keys()}"
|
151
153
|
)
|
@@ -171,4 +173,5 @@ question_purpose = {
|
|
171
173
|
|
172
174
|
if __name__ == "__main__":
|
173
175
|
import doctest
|
176
|
+
|
174
177
|
doctest.testmod()
|
edsl/scenarios/file_store.py
CHANGED
@@ -512,6 +512,75 @@ class FileStore(Scenario):
|
|
512
512
|
)
|
513
513
|
return info
|
514
514
|
|
515
|
+
def offload(self, inplace=False) -> "FileStore":
|
516
|
+
"""
|
517
|
+
Offloads base64-encoded content from the FileStore by replacing 'base64_string'
|
518
|
+
with 'offloaded'. This reduces memory usage.
|
519
|
+
|
520
|
+
Args:
|
521
|
+
inplace (bool): If True, modify the current FileStore. If False, return a new one.
|
522
|
+
|
523
|
+
Returns:
|
524
|
+
FileStore: The modified FileStore (either self or a new instance).
|
525
|
+
"""
|
526
|
+
if inplace:
|
527
|
+
if hasattr(self, "base64_string"):
|
528
|
+
self.base64_string = "offloaded"
|
529
|
+
return self
|
530
|
+
else:
|
531
|
+
# Create a copy and offload it
|
532
|
+
file_store_dict = self.to_dict()
|
533
|
+
if "base64_string" in file_store_dict:
|
534
|
+
file_store_dict["base64_string"] = "offloaded"
|
535
|
+
return self.__class__.from_dict(file_store_dict)
|
536
|
+
|
537
|
+
def save_to_gcs_bucket(self, signed_url: str) -> dict:
|
538
|
+
"""
|
539
|
+
Saves the FileStore's file content to a Google Cloud Storage bucket using a signed URL.
|
540
|
+
|
541
|
+
Args:
|
542
|
+
signed_url (str): The signed URL for uploading to GCS bucket
|
543
|
+
|
544
|
+
Returns:
|
545
|
+
dict: Response from the GCS upload operation
|
546
|
+
|
547
|
+
Raises:
|
548
|
+
ValueError: If base64_string is offloaded or missing
|
549
|
+
requests.RequestException: If the upload fails
|
550
|
+
"""
|
551
|
+
import requests
|
552
|
+
import base64
|
553
|
+
|
554
|
+
# Check if content is available
|
555
|
+
if not hasattr(self, "base64_string") or self.base64_string == "offloaded":
|
556
|
+
raise ValueError(
|
557
|
+
"File content is not available (offloaded or missing). Cannot upload to GCS."
|
558
|
+
)
|
559
|
+
|
560
|
+
# Decode base64 content to bytes
|
561
|
+
try:
|
562
|
+
file_content = base64.b64decode(self.base64_string)
|
563
|
+
except Exception as e:
|
564
|
+
raise ValueError(f"Failed to decode base64 content: {e}")
|
565
|
+
|
566
|
+
# Prepare headers with proper content type
|
567
|
+
headers = {
|
568
|
+
"Content-Type": self.mime_type or "application/octet-stream",
|
569
|
+
"Content-Length": str(len(file_content)),
|
570
|
+
}
|
571
|
+
|
572
|
+
# Upload to GCS using the signed URL
|
573
|
+
response = requests.put(signed_url, data=file_content, headers=headers)
|
574
|
+
response.raise_for_status()
|
575
|
+
|
576
|
+
return {
|
577
|
+
"status": "success",
|
578
|
+
"status_code": response.status_code,
|
579
|
+
"file_size": len(file_content),
|
580
|
+
"mime_type": self.mime_type,
|
581
|
+
"file_extension": self.suffix,
|
582
|
+
}
|
583
|
+
|
515
584
|
@classmethod
|
516
585
|
def pull(cls, url_or_uuid: Union[str, UUID]) -> "FileStore":
|
517
586
|
"""
|
edsl/scenarios/scenario.py
CHANGED
@@ -280,6 +280,18 @@ class Scenario(Base, UserDict):
|
|
280
280
|
|
281
281
|
target = self if inplace else Scenario()
|
282
282
|
|
283
|
+
# First check if this Scenario itself has a base64_string (e.g., from FileStore.to_dict())
|
284
|
+
if "base64_string" in self and isinstance(self.get("base64_string"), str):
|
285
|
+
# This is likely a Scenario created from FileStore.to_dict()
|
286
|
+
if inplace:
|
287
|
+
self["base64_string"] = "offloaded"
|
288
|
+
else:
|
289
|
+
# Copy all keys to target
|
290
|
+
for k, v in self.items():
|
291
|
+
target[k] = v
|
292
|
+
target["base64_string"] = "offloaded"
|
293
|
+
return target
|
294
|
+
|
283
295
|
for key, value in self.items():
|
284
296
|
if isinstance(value, FileStore):
|
285
297
|
file_store_dict = value.to_dict()
|
@@ -297,6 +309,227 @@ class Scenario(Base, UserDict):
|
|
297
309
|
|
298
310
|
return target
|
299
311
|
|
312
|
+
def save_to_gcs_bucket(self, signed_url_or_dict) -> dict:
|
313
|
+
"""
|
314
|
+
Saves FileStore objects contained within this Scenario to a Google Cloud Storage bucket.
|
315
|
+
|
316
|
+
This method finds all FileStore objects in the Scenario and uploads them to GCS using
|
317
|
+
the provided signed URL(s). If the Scenario itself was created from a FileStore (has
|
318
|
+
base64_string as a top-level key), it uploads that content directly.
|
319
|
+
|
320
|
+
Args:
|
321
|
+
signed_url_or_dict: Either:
|
322
|
+
- str: Single signed URL (for single FileStore or Scenario from FileStore)
|
323
|
+
- dict: Mapping of scenario keys to signed URLs for multiple FileStore objects
|
324
|
+
e.g., {"video": "signed_url_1", "image": "signed_url_2"}
|
325
|
+
|
326
|
+
Returns:
|
327
|
+
dict: Summary of upload operations performed
|
328
|
+
|
329
|
+
Raises:
|
330
|
+
ValueError: If no uploadable content found or content is offloaded
|
331
|
+
requests.RequestException: If any upload fails
|
332
|
+
"""
|
333
|
+
from edsl.scenarios import FileStore
|
334
|
+
import requests
|
335
|
+
import base64
|
336
|
+
|
337
|
+
upload_results = []
|
338
|
+
|
339
|
+
# Case 1: This Scenario was created from a FileStore (has direct base64_string)
|
340
|
+
if "base64_string" in self and isinstance(self.get("base64_string"), str):
|
341
|
+
if self["base64_string"] == "offloaded":
|
342
|
+
raise ValueError("File content is offloaded. Cannot upload to GCS.")
|
343
|
+
|
344
|
+
# For single FileStore scenario, expect string URL
|
345
|
+
if isinstance(signed_url_or_dict, dict):
|
346
|
+
raise ValueError(
|
347
|
+
"For Scenario created from FileStore, provide a single signed URL string, not a dictionary."
|
348
|
+
)
|
349
|
+
|
350
|
+
signed_url = signed_url_or_dict
|
351
|
+
|
352
|
+
# Get file info from Scenario keys
|
353
|
+
mime_type = self.get("mime_type", "application/octet-stream")
|
354
|
+
suffix = self.get("suffix", "")
|
355
|
+
|
356
|
+
# Decode and upload
|
357
|
+
try:
|
358
|
+
file_content = base64.b64decode(self["base64_string"])
|
359
|
+
except Exception as e:
|
360
|
+
raise ValueError(f"Failed to decode base64 content: {e}")
|
361
|
+
|
362
|
+
headers = {
|
363
|
+
"Content-Type": mime_type,
|
364
|
+
"Content-Length": str(len(file_content)),
|
365
|
+
}
|
366
|
+
|
367
|
+
response = requests.put(signed_url, data=file_content, headers=headers)
|
368
|
+
response.raise_for_status()
|
369
|
+
|
370
|
+
upload_results.append(
|
371
|
+
{
|
372
|
+
"type": "scenario_filestore_content",
|
373
|
+
"status": "success",
|
374
|
+
"status_code": response.status_code,
|
375
|
+
"file_size": len(file_content),
|
376
|
+
"mime_type": mime_type,
|
377
|
+
"file_extension": suffix,
|
378
|
+
}
|
379
|
+
)
|
380
|
+
|
381
|
+
# Case 2: Find FileStore objects in Scenario values
|
382
|
+
else:
|
383
|
+
# Collect all FileStore keys first
|
384
|
+
filestore_keys = [
|
385
|
+
key for key, value in self.items() if isinstance(value, FileStore)
|
386
|
+
]
|
387
|
+
|
388
|
+
if not filestore_keys:
|
389
|
+
raise ValueError("No FileStore objects found in Scenario to upload.")
|
390
|
+
|
391
|
+
# Handle URL parameter
|
392
|
+
if isinstance(signed_url_or_dict, str):
|
393
|
+
# Single URL provided for multiple FileStore objects - this will cause overwrites
|
394
|
+
if len(filestore_keys) > 1:
|
395
|
+
raise ValueError(
|
396
|
+
f"Multiple FileStore objects found ({filestore_keys}) but only one signed URL provided. "
|
397
|
+
f"Provide a dictionary mapping keys to URLs to avoid overwrites: "
|
398
|
+
f"{{'{filestore_keys[0]}': 'url1', '{filestore_keys[1]}': 'url2', ...}}"
|
399
|
+
)
|
400
|
+
|
401
|
+
# Single FileStore object, single URL is fine
|
402
|
+
url_mapping = {filestore_keys[0]: signed_url_or_dict}
|
403
|
+
|
404
|
+
elif isinstance(signed_url_or_dict, dict):
|
405
|
+
# Dictionary of URLs provided
|
406
|
+
missing_keys = set(filestore_keys) - set(signed_url_or_dict.keys())
|
407
|
+
if missing_keys:
|
408
|
+
raise ValueError(
|
409
|
+
f"Missing signed URLs for FileStore keys: {list(missing_keys)}"
|
410
|
+
)
|
411
|
+
|
412
|
+
extra_keys = set(signed_url_or_dict.keys()) - set(filestore_keys)
|
413
|
+
if extra_keys:
|
414
|
+
raise ValueError(
|
415
|
+
f"Signed URLs provided for non-FileStore keys: {list(extra_keys)}"
|
416
|
+
)
|
417
|
+
|
418
|
+
url_mapping = signed_url_or_dict
|
419
|
+
|
420
|
+
else:
|
421
|
+
raise ValueError(
|
422
|
+
"signed_url_or_dict must be either a string or a dictionary"
|
423
|
+
)
|
424
|
+
|
425
|
+
# Upload each FileStore object
|
426
|
+
for key, value in self.items():
|
427
|
+
if isinstance(value, FileStore):
|
428
|
+
try:
|
429
|
+
result = value.save_to_gcs_bucket(url_mapping[key])
|
430
|
+
result["scenario_key"] = key
|
431
|
+
result["type"] = "filestore_object"
|
432
|
+
upload_results.append(result)
|
433
|
+
except Exception as e:
|
434
|
+
upload_results.append(
|
435
|
+
{
|
436
|
+
"scenario_key": key,
|
437
|
+
"type": "filestore_object",
|
438
|
+
"status": "error",
|
439
|
+
"error": str(e),
|
440
|
+
}
|
441
|
+
)
|
442
|
+
|
443
|
+
return {
|
444
|
+
"total_uploads": len(upload_results),
|
445
|
+
"successful_uploads": len(
|
446
|
+
[r for r in upload_results if r.get("status") == "success"]
|
447
|
+
),
|
448
|
+
"failed_uploads": len(
|
449
|
+
[r for r in upload_results if r.get("status") == "error"]
|
450
|
+
),
|
451
|
+
"upload_details": upload_results,
|
452
|
+
}
|
453
|
+
|
454
|
+
def get_filestore_info(self) -> dict:
|
455
|
+
"""
|
456
|
+
Returns information about FileStore objects present in this Scenario.
|
457
|
+
|
458
|
+
This method is useful for determining how many signed URLs need to be generated
|
459
|
+
and what file extensions/types are present before calling save_to_gcs_bucket().
|
460
|
+
|
461
|
+
Returns:
|
462
|
+
dict: Information about FileStore objects containing:
|
463
|
+
- total_count: Total number of FileStore objects
|
464
|
+
- filestore_keys: List of scenario keys that contain FileStore objects
|
465
|
+
- file_extensions: Dictionary mapping keys to file extensions
|
466
|
+
- file_types: Dictionary mapping keys to MIME types
|
467
|
+
- is_filestore_scenario: Boolean indicating if this Scenario was created from a FileStore
|
468
|
+
- summary: Human-readable summary of files
|
469
|
+
|
470
|
+
|
471
|
+
"""
|
472
|
+
from edsl.scenarios import FileStore
|
473
|
+
|
474
|
+
# Check if this Scenario was created from a FileStore
|
475
|
+
is_filestore_scenario = "base64_string" in self and isinstance(
|
476
|
+
self.get("base64_string"), str
|
477
|
+
)
|
478
|
+
|
479
|
+
if is_filestore_scenario:
|
480
|
+
# Single FileStore scenario
|
481
|
+
return {
|
482
|
+
"total_count": 1,
|
483
|
+
"filestore_keys": ["filestore_content"],
|
484
|
+
"file_extensions": {"filestore_content": self.get("suffix", "")},
|
485
|
+
"file_types": {
|
486
|
+
"filestore_content": self.get(
|
487
|
+
"mime_type", "application/octet-stream"
|
488
|
+
)
|
489
|
+
},
|
490
|
+
"is_filestore_scenario": True,
|
491
|
+
"summary": f"Single FileStore content with extension '{self.get('suffix', 'unknown')}'",
|
492
|
+
}
|
493
|
+
|
494
|
+
# Regular Scenario with FileStore objects as values
|
495
|
+
filestore_info = {}
|
496
|
+
file_extensions = {}
|
497
|
+
file_types = {}
|
498
|
+
|
499
|
+
for key, value in self.items():
|
500
|
+
if isinstance(value, FileStore):
|
501
|
+
filestore_info[key] = {
|
502
|
+
"extension": getattr(value, "suffix", ""),
|
503
|
+
"mime_type": getattr(
|
504
|
+
value, "mime_type", "application/octet-stream"
|
505
|
+
),
|
506
|
+
"binary": getattr(value, "binary", True),
|
507
|
+
"path": getattr(value, "path", "unknown"),
|
508
|
+
}
|
509
|
+
file_extensions[key] = getattr(value, "suffix", "")
|
510
|
+
file_types[key] = getattr(
|
511
|
+
value, "mime_type", "application/octet-stream"
|
512
|
+
)
|
513
|
+
|
514
|
+
# Generate summary
|
515
|
+
if filestore_info:
|
516
|
+
ext_summary = [f"{key}({ext})" for key, ext in file_extensions.items()]
|
517
|
+
summary = (
|
518
|
+
f"{len(filestore_info)} FileStore objects: {', '.join(ext_summary)}"
|
519
|
+
)
|
520
|
+
else:
|
521
|
+
summary = "No FileStore objects found"
|
522
|
+
|
523
|
+
return {
|
524
|
+
"total_count": len(filestore_info),
|
525
|
+
"filestore_keys": list(filestore_info.keys()),
|
526
|
+
"file_extensions": file_extensions,
|
527
|
+
"file_types": file_types,
|
528
|
+
"is_filestore_scenario": False,
|
529
|
+
"detailed_info": filestore_info,
|
530
|
+
"summary": summary,
|
531
|
+
}
|
532
|
+
|
300
533
|
def to_dict(
|
301
534
|
self, add_edsl_version: bool = True, offload_base64: bool = False
|
302
535
|
) -> dict:
|
@@ -1334,7 +1334,6 @@ class DelimitedFileSource(Source):
|
|
1334
1334
|
header_counts = defaultdict(lambda: 0)
|
1335
1335
|
new_header = []
|
1336
1336
|
for h in header:
|
1337
|
-
print(header_counts)
|
1338
1337
|
if header_counts[h] >= 1:
|
1339
1338
|
new_header.append(f"{h}_{header_counts[h]}")
|
1340
1339
|
warnings.warn(
|
@@ -1,13 +1,13 @@
|
|
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=cwSKWX9cG1qs0I6C99TSkty5QpTa10uiqSeiXnsoOg0,23
|
4
4
|
edsl/agents/__init__.py,sha256=AyhfXjygRHT1Pd9w16lcu5Bu0jnBmMPz86aKP1uRL3Y,93
|
5
5
|
edsl/agents/agent.py,sha256=scTDLrvO5NAUQD9vGVzrPep9-wVcQoNbnQ0GoB5FgH4,58123
|
6
6
|
edsl/agents/agent_list.py,sha256=EV-O1G0Atn1iwpNR78WXLJ-h_kAFpO74LyzCMe_5zLw,26916
|
7
7
|
edsl/agents/descriptors.py,sha256=TfFQWJqhqTWyH89DkNmK6qtH3xV2fUyW9FbI5KnZXv0,4592
|
8
8
|
edsl/agents/exceptions.py,sha256=7KMAtAHKqlkVkd_iVZC_mWXQnzDPV0V_n2iXaGAQgzc,5661
|
9
9
|
edsl/base/__init__.py,sha256=h119NxrAJOV92jnX7ussXNjKFXqzySVGOjMG3G7Zkzc,992
|
10
|
-
edsl/base/base_class.py,sha256=
|
10
|
+
edsl/base/base_class.py,sha256=09icKXkd8J1mDpP1TLpFZvPVCp8YUiY4MOSW-eu3omU,51306
|
11
11
|
edsl/base/base_exception.py,sha256=gwk4mNoS3TBe6446NiQeSrUrjUqjlB3_fcDFgV90Dms,7644
|
12
12
|
edsl/base/data_transfer_models.py,sha256=JpEnlgdQ5_URixzZUr7MJuAY4U6obPo0rWfzDl39WNg,3934
|
13
13
|
edsl/base/enums.py,sha256=46mqtWjeiL6NTsN8j-zGfY8QNOVXO4sVb1p1MjmD1N4,6613
|
@@ -40,12 +40,12 @@ edsl/conversation/exceptions.py,sha256=DoUCg-ymqGOjOl0cpGT8-sNRVsr3SEwdxGAKtdeZ2
|
|
40
40
|
edsl/conversation/mug_negotiation.py,sha256=do3PTykM6A2cDGOcsohlevRgLpCICoPx8B0WIYe6hy8,2518
|
41
41
|
edsl/conversation/next_speaker_utilities.py,sha256=bqr5JglCd6bdLc9IZ5zGOAsmN2F4ERiubSMYvZIG7qk,3629
|
42
42
|
edsl/coop/__init__.py,sha256=DU2w1Nu8q6tMAa3xoPC722RrvGhmB_UgUUBJDUywsKY,1542
|
43
|
-
edsl/coop/coop.py,sha256=
|
43
|
+
edsl/coop/coop.py,sha256=p6oBuot318sku3BqUXwQAYKwxHB3viarnloMdut5Nug,135375
|
44
44
|
edsl/coop/coop_functions.py,sha256=d31kddfj9MVZaMhqwUvkSIBwrdCTQglIvFWVfUr4NuE,688
|
45
|
-
edsl/coop/coop_jobs_objects.py,sha256=
|
45
|
+
edsl/coop/coop_jobs_objects.py,sha256=Xx6YF1Uk97d3RRhNDe7kN6BA9DXzYn_TWENd6DhZgms,1738
|
46
46
|
edsl/coop/coop_objects.py,sha256=_cEspdAxh7BT672poxb0HsjU-QZ4Kthg-tKDvZ6I_v0,859
|
47
47
|
edsl/coop/coop_prolific_filters.py,sha256=gSQI25tg2f-ouj3Gh_yMl4ppzMnGYt13kPRg-ICKZ64,6489
|
48
|
-
edsl/coop/coop_regular_objects.py,sha256=
|
48
|
+
edsl/coop/coop_regular_objects.py,sha256=kKKHapF1xWx8jzgmFcY66HOr2cIqjYMcBI71Vq3nNN0,836
|
49
49
|
edsl/coop/ep_key_handling.py,sha256=X0tskEaYKsRIbFUijaCL69uHYpLJcLbYFITzAu3PGJE,7872
|
50
50
|
edsl/coop/exceptions.py,sha256=EY3eNTeJM15VzFnag93hgmiqn4pR3Y-6nS9ixKGIhM8,8874
|
51
51
|
edsl/coop/price_fetcher.py,sha256=uvEPgKaSRsFq-ouRl5W9aksawUkJg9Lo7ucSePecwa4,4735
|
@@ -134,7 +134,7 @@ edsl/jobs/decorators.py,sha256=0Eot9pFPsWmQIJAafNd0f5hdb9RUAFp_hGMmSUTJ_C8,3272
|
|
134
134
|
edsl/jobs/exceptions.py,sha256=5lktTya2VgiBR5Bd977tG2xHdrMjDqhPhQO17O6jIdc,7220
|
135
135
|
edsl/jobs/fetch_invigilator.py,sha256=nzXAIulvOvuDpRDEN5TDNmEfikUEwrnS_XCtnYG2uPQ,2795
|
136
136
|
edsl/jobs/html_table_job_logger.py,sha256=2ErAIi_Dgv_Y3l-AZ2bPUJO_X8hSrPfeFT9lEjt8X4g,34762
|
137
|
-
edsl/jobs/jobs.py,sha256=
|
137
|
+
edsl/jobs/jobs.py,sha256=lQf16XEeDYAoUjbM8AWqEvHT7-S5sKwqIBMcweeY5oI,45393
|
138
138
|
edsl/jobs/jobs_checks.py,sha256=bfPJ3hQ4qvRBhyte4g-4J8zExJxJr3nlLHmtVmFPJcQ,5390
|
139
139
|
edsl/jobs/jobs_component_constructor.py,sha256=9956UURv3eo-cURNPd4EV8wAQsY-AlEtQRmBu1nCOH8,6982
|
140
140
|
edsl/jobs/jobs_interview_constructor.py,sha256=8nIhhwBQWH_aZ9ZWjvRgOL0y2y6juRTb3pVngQ9Cs8g,2017
|
@@ -143,7 +143,7 @@ edsl/jobs/jobs_remote_inference_logger.py,sha256=4I3DjIzxfWHjWBr7o_JPhj9f8M4LuuP
|
|
143
143
|
edsl/jobs/jobs_runner_status.py,sha256=gW8EA-BAKpBvahqRipzomALEAQizd24aRW8G2y7faLQ,11905
|
144
144
|
edsl/jobs/jobs_status_enums.py,sha256=8Kgtr-ffcGGniQ2x5gCOqwURb_HaBWmYcWbUB_KTCY0,214
|
145
145
|
edsl/jobs/progress_bar_manager.py,sha256=d8wuZf7SHq3LCA36JIv1sfYymyHFOUsYRSRlRpR6K04,2832
|
146
|
-
edsl/jobs/remote_inference.py,sha256=
|
146
|
+
edsl/jobs/remote_inference.py,sha256=yRwFabzuBLrwDkKdEdy8PweHKi7-FnShF7t-5-2aTqI,24156
|
147
147
|
edsl/jobs/results_exceptions_handler.py,sha256=VCtnd60xwdFznzGhtXPbxLmyVf3kIjR2419LUJdFjEQ,3053
|
148
148
|
edsl/key_management/__init__.py,sha256=JiOJ71Ly9aw-tVYbWZu-qRjsW4QETYMQ9IJjsKgW1DQ,1274
|
149
149
|
edsl/key_management/exceptions.py,sha256=dDtoDh1UL52BUBrAlCIc_McgtZCAQkUx6onoSz26qeM,2158
|
@@ -180,7 +180,7 @@ edsl/plugins/plugin_manager.py,sha256=ifuJLgcySmLvGOc8ka8tSj-3d6ju0NknEK22pLF1L8
|
|
180
180
|
edsl/plugins/plugins_registry.py,sha256=stAaq6vkuurHc3ViHrLj5g2VomMpsLD9ufa-k-HHfgk,5165
|
181
181
|
edsl/prompts/__init__.py,sha256=4UREcqKC6SIfYykwZbaCeXI5hEil0u2x5GQKasn_NLU,653
|
182
182
|
edsl/prompts/exceptions.py,sha256=AcQCy8JGmS8ODCvRtu4aCH14OEI-oYxF0tX-ZAZ3Puk,4460
|
183
|
-
edsl/prompts/prompt.py,sha256=
|
183
|
+
edsl/prompts/prompt.py,sha256=V_duf2qzvBd_fPZLD2bt1WelAfJQT8H3bJ8WoysqkXI,14537
|
184
184
|
edsl/questions/ExceptionExplainer.py,sha256=BgM80FRPJjS_TrY6XaVmlT666MzY9DEagviGQj9-WEQ,2868
|
185
185
|
edsl/questions/HTMLQuestion.py,sha256=lx3Sysm6fMZmFc9hifnkGslt7ZBpDEvziM9-IJFMJLU,3238
|
186
186
|
edsl/questions/Quick.py,sha256=HRLT2Lmhd1Gj4ggkrpCMYhzeWsRwlQaigu2EzdiXb5Q,1717
|
@@ -218,7 +218,7 @@ edsl/questions/question_multiple_choice.py,sha256=uTWZ0FGE8czIxmiZ_6mvc8KR5efpat
|
|
218
218
|
edsl/questions/question_multiple_choice_with_other.py,sha256=J0_3V5SfetQzqqVMgTIZ5TUwiY4X-bMCogSqquG0tzQ,23624
|
219
219
|
edsl/questions/question_numerical.py,sha256=2b41BzrjcwnrVw98pDWjEQIw7Ge75chlgbbS2Jr25Wg,17549
|
220
220
|
edsl/questions/question_rank.py,sha256=6oihp85lujt0EAoc7ZxZZf-3p8m-hqGBqSbJCRDUarM,23195
|
221
|
-
edsl/questions/question_registry.py,sha256=
|
221
|
+
edsl/questions/question_registry.py,sha256=YsuwHKwBpcYtt-XM8EfprF0lo_79QSJ36abUnAE6ZtE,6328
|
222
222
|
edsl/questions/question_top_k.py,sha256=zAscGTBQisYX2jV9l3MCLz1PjziMqBlz81gybc-dot4,3233
|
223
223
|
edsl/questions/question_yes_no.py,sha256=nF0RowcQucROyagcaCUi-yOveS8jfoiyOAlgjOfVnc8,2689
|
224
224
|
edsl/questions/register_questions_meta.py,sha256=Ykf0zdVaOolI3YOVdEuVwCd3JByiggV008nBfgnBMfI,2697
|
@@ -291,7 +291,7 @@ edsl/scenarios/directory_scanner.py,sha256=xv-3HHRPsyGa8m6mHpqLjK-UBC-nhG9gz3VC5
|
|
291
291
|
edsl/scenarios/document_chunker.py,sha256=EpB0V0oxLzpKntl00Qa3VZNPS7sg9aXdYyqKxhFFzTM,7680
|
292
292
|
edsl/scenarios/exceptions.py,sha256=FeORBm90UthKHDp7cE8I7KJgyA3-pFKNpoivZRr8ifc,10636
|
293
293
|
edsl/scenarios/file_methods.py,sha256=LkN7mZsadRaiNhvKPP_jY7OhUMEsfhEEFY-hpnwdplM,2794
|
294
|
-
edsl/scenarios/file_store.py,sha256=
|
294
|
+
edsl/scenarios/file_store.py,sha256=s4FpEHAjykaI1qd32cXOnv95jX2EIy01AHiELtwZCto,35376
|
295
295
|
edsl/scenarios/handlers/__init__.py,sha256=_-A6vXzQPKga7fDyteDt1QPA6lDwmgERJKG8SrdhYxQ,965
|
296
296
|
edsl/scenarios/handlers/csv_file_store.py,sha256=kXOms0ph5JJj6jSbpfQ-SZjuT4vvSRhq5AGpv1L4TPQ,1369
|
297
297
|
edsl/scenarios/handlers/docx_file_store.py,sha256=KSKAAUIWF2K5xr92nx7UGQ9djgtDX4ke-Eyik8QAdlQ,2155
|
@@ -309,7 +309,7 @@ edsl/scenarios/handlers/sql_file_store.py,sha256=wa_Qw1-bk-tHhtQrp1IAxSAROygEQ5F
|
|
309
309
|
edsl/scenarios/handlers/sqlite_file_store.py,sha256=rwsfxD5G_XNEa-aRCx6A83lW0i2OiS51EzYsJeTE7ps,4936
|
310
310
|
edsl/scenarios/handlers/txt_file_store.py,sha256=oGMqm2X_dWTt0W2e2zDung2i_A_z2mMmm4rrQImnVtU,980
|
311
311
|
edsl/scenarios/handlers/webm_file_store.py,sha256=UG3sPwsxbZAjM1H9rbpdkvXMrS3iRbaaN-4VNGh3JX8,3659
|
312
|
-
edsl/scenarios/scenario.py,sha256=
|
312
|
+
edsl/scenarios/scenario.py,sha256=W1wWo-wh5hfUb42C8YHRq8Qu2spvgL2clp64bupIPYk,48840
|
313
313
|
edsl/scenarios/scenario_join.py,sha256=1r_czZctN7JKbw38bQolKdz0kBaMqhWzo8IsxzHK1TY,5409
|
314
314
|
edsl/scenarios/scenario_list.py,sha256=tPpFyNDTiMEkl59DA8VTBgGBDt67f1E8Od5qHMXULzM,88787
|
315
315
|
edsl/scenarios/scenario_list_gc_test.py,sha256=VaZBg_GjfSaM92Gj3eiSt3aQ_rECDfD339ZCTqryfdc,4676
|
@@ -317,7 +317,7 @@ edsl/scenarios/scenario_list_memory_test.py,sha256=l_PeTJkh0MYQoRLIiFOI8hmzEyjf8
|
|
317
317
|
edsl/scenarios/scenario_list_pdf_tools.py,sha256=sehQro5PzJ7Y4Ck9VJ8HTxKN8HSbk3aDipVYuxaJbdI,7686
|
318
318
|
edsl/scenarios/scenario_list_source_refactor.md,sha256=LFbkxOgc7eWtsX1zQlfYwqWW4pLJNTTXPP7zsyB3iQY,1554
|
319
319
|
edsl/scenarios/scenario_selector.py,sha256=7Hm4DitY7X6tKTAofhMMHIFkZf6qXFSminL2wBrUJ2w,6037
|
320
|
-
edsl/scenarios/scenario_source.py,sha256=
|
320
|
+
edsl/scenarios/scenario_source.py,sha256=Uua3bOHCyuYjbnt5dj388iNHqNX99vCyCUhuqCA7Wmg,73211
|
321
321
|
edsl/scenarios/tests/test_scenario_list_sources.py,sha256=2JBxBn-l_X22WPTKxMTlU10swkfN1DF6c_2-BONEy_E,2135
|
322
322
|
edsl/surveys/__init__.py,sha256=04hdYqBBdb9jtZVkZffbFszr0yzXVPPFiI-XhgZRLu0,327
|
323
323
|
edsl/surveys/base.py,sha256=XJHGEbbsH6hlYYkmI4isVLD8guLz8BdhR-eQRL78mc4,1115
|
@@ -384,8 +384,8 @@ edsl/utilities/restricted_python.py,sha256=248N2p5EWHDSpcK1G-q7DUoJeWy4sB6aO-RV0
|
|
384
384
|
edsl/utilities/template_loader.py,sha256=SCAcnTnxNQ67MNSkmfz7F-S_u2peyGn2j1oRIqi1wfg,870
|
385
385
|
edsl/utilities/utilities.py,sha256=irHheAGOnl_6RwI--Hi9StVzvsHcWCqB48PWsWJQYOw,12045
|
386
386
|
edsl/utilities/wikipedia.py,sha256=I3Imbz3fzbaoA0ZLDsWUO2YpP_ovvaqtu-yd2Ye1BB0,6933
|
387
|
-
edsl-0.1.
|
388
|
-
edsl-0.1.
|
389
|
-
edsl-0.1.
|
390
|
-
edsl-0.1.
|
391
|
-
edsl-0.1.
|
387
|
+
edsl-0.1.62.dist-info/LICENSE,sha256=_qszBDs8KHShVYcYzdMz3HNMtH-fKN_p5zjoVAVumFc,1111
|
388
|
+
edsl-0.1.62.dist-info/METADATA,sha256=4I8R2M4qeuE3YMXvGNls3yNSvjWnHmx6jkIxZdR8azo,12076
|
389
|
+
edsl-0.1.62.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
390
|
+
edsl-0.1.62.dist-info/entry_points.txt,sha256=JnG7xqMtHaQu9BU-yPATxdyCeA48XJpuclnWCqMfIMU,38
|
391
|
+
edsl-0.1.62.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|